Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add means to opt out of ssr partially or completely #12

Merged
merged 2 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

68 changes: 42 additions & 26 deletions crates/stackable-backend/src/endpoint.rs
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,10 @@ where
pub fn new() -> Self
where
CTX: Default,
{
Endpoint::<COMP, CTX>::with_create_context(|m| m.with_context(CTX::default()))
}

pub fn with_create_context<F>(create_context: F) -> Self
where
F: 'static + Clone + Send + Fn(ServerAppProps<()>) -> ServerAppProps<CTX>,
{
Self {
affix_context: SendFn::<ServerAppProps<()>, ServerAppProps<CTX>>::new(move || {
Box::new(create_context.clone())
Box::new(|m| m.with_context(CTX::default()))
}),
bridge: None,
#[cfg(feature = "warp-filter")]
Expand All @@ -75,6 +68,24 @@ where
}
}

pub fn with_append_context<F, C>(self, append_context: F) -> Endpoint<COMP, C>
where
F: 'static + Clone + Send + Fn(ServerAppProps<()>) -> ServerAppProps<C>,
C: 'static,
{
Endpoint {
affix_context: SendFn::<ServerAppProps<()>, ServerAppProps<C>>::new(move || {
Box::new(append_context.clone())
}),
bridge: self.bridge,
#[cfg(feature = "warp-filter")]
frontend: self.frontend,
#[cfg(feature = "warp-filter")]
auto_refresh: self.auto_refresh,
_marker: PhantomData,
}
}

pub fn with_bridge(mut self, bridge: Bridge) -> Self {
self.bridge = Some(bridge);
self
Expand Down Expand Up @@ -181,28 +192,33 @@ mod feat_warp_filter {

let create_render_inner = move |props, tx: sync_oneshot::Sender<String>| async move {
let props = (affix_context.get())(props);
let (reader, writer) = render_static();

let mut body_s = yew::LocalServerRenderer::<StackableRoot<COMP, CTX>>::with_props(
StackableRootProps {
server_app_props: props,
helmet_writer: writer,
bridge,
},
)
.render()
.await;

let mut head_s = String::new();
let helmet_tags = reader.render().await;
let mut body_s = String::new();

for tag in helmet_tags {
let _ = tag.write_static(&mut head_s);
if !props.is_client_only() {
let (reader, writer) = render_static();

body_s = yew::LocalServerRenderer::<StackableRoot<COMP, CTX>>::with_props(
StackableRootProps {
server_app_props: props,
helmet_writer: writer,
bridge,
},
)
.render()
.await;

let helmet_tags = reader.render().await;

for tag in helmet_tags {
let _ = tag.write_static(&mut head_s);
}
let _ = write!(
&mut head_s,
r#"<meta name="stackable-mode" content="hydrate">"#
);
}
let _ = write!(
&mut head_s,
r#"<meta name="stackable-mode" content="hydrate">"#
);

// With development server, we read index.html every time.
let index_html_s = match index_html {
Expand Down
13 changes: 13 additions & 0 deletions crates/stackable-backend/src/props.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct Inner {
pub struct ServerAppProps<T = ()> {
inner: Arc<Inner>,
context: Arc<T>,
client_only: bool,
}

impl<T> ServerAppProps<T> {
Expand Down Expand Up @@ -67,6 +68,7 @@ impl<T> Clone for ServerAppProps<T> {
Self {
inner: self.inner.clone(),
context: self.context.clone(),
client_only: self.client_only,
}
}
}
Expand All @@ -76,8 +78,18 @@ impl<T> ServerAppProps<T> {
ServerAppProps {
inner: self.inner,
context: context.into(),
client_only: false,
}
}

pub fn client_only(mut self) -> Self {
self.client_only = true;
self
}

pub(crate) fn is_client_only(&self) -> bool {
self.client_only
}
}

#[cfg(feature = "warp-filter")]
Expand All @@ -95,6 +107,7 @@ mod feat_warp_filter {
}
.into(),
context: ().into(),
client_only: false,
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions crates/stackable-backend/src/root.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use yew_router::Router;
use crate::props::ServerAppProps;

#[derive(Properties)]
pub struct StackableRootProps<CTX> {
pub(crate) struct StackableRootProps<CTX> {
pub helmet_writer: StaticWriter,
pub server_app_props: ServerAppProps<CTX>,
pub bridge: Bridge,
Expand Down Expand Up @@ -76,7 +76,7 @@ where
}

#[function_component]
pub fn StackableRoot<COMP, CTX>(props: &StackableRootProps<CTX>) -> Html
pub(crate) fn StackableRoot<COMP, CTX>(props: &StackableRootProps<CTX>) -> Html
where
COMP: BaseComponent<Properties = ServerAppProps<CTX>>,
CTX: 'static,
Expand Down
25 changes: 25 additions & 0 deletions crates/stackable-frontend/src/components/client_only.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
use std::ops::Deref;

use yew::html::ChildrenProps;
use yew::prelude::*;

/// A component that automatically excludes its children from server-side rendering.
#[function_component]
pub fn ClientOnly(props: &ChildrenProps) -> Html {
let should_render = use_state(|| false);

// Effects are only run on the client side.
{
use_effect_with_deps(
|should_render_setter| {
should_render_setter.set(true);
},
should_render.setter(),
);
}

match should_render.deref() {
true => html! {<>{props.children.clone()}</>},
false => Html::default(),
}
}
3 changes: 3 additions & 0 deletions crates/stackable-frontend/src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
mod client_only;

pub use client_only::ClientOnly;
1 change: 1 addition & 0 deletions crates/stackable-frontend/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use stackable_bridge::Bridge;
use yew::prelude::*;

use crate::root::{StackableRoot, StackableRootProps};
pub mod components;
mod root;
pub mod trace;

Expand Down
1 change: 1 addition & 0 deletions examples/fullstack/view/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ example-fullstack-api = { path = "../api" }
stackable-bridge = { version = "0.1.0", path = "../../../crates/stackable-bridge" }
time = { version = "0.3", features = ["wasm-bindgen", "serde-human-readable", "macros"] }
tracing = "0.1.37"
bounce = { version = "0.6.0", features = ["helmet"] }

[dependencies.web-sys]
version = "0.3"
Expand Down
20 changes: 13 additions & 7 deletions examples/fullstack/view/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![deny(clippy::all)]
#![deny(missing_debug_implementations)]

use bounce::helmet::Helmet;
use yew::prelude::*;

mod pages;
Expand All @@ -11,12 +12,17 @@ pub fn Main() -> Html {
let fallback = html! {<div class="time-loading">{"Loading..."}</div>};

html! {
<div class="container">
<div class="title">{"Welcome to Stackable!"}</div>
<Suspense {fallback}>
<ServerTime />
</Suspense>
<Greeting />
</div>
<>
<Helmet>
<title>{"Welcome to Stackable!"}</title>
</Helmet>
<div class="container">
<div class="title">{"Welcome to Stackable!"}</div>
<Suspense {fallback}>
<ServerTime />
</Suspense>
<Greeting />
</div>
</>
}
}