Skip to content

Commit

Permalink
feat: add means to opt out of ssr partially or completely (#12)
Browse files Browse the repository at this point in the history
* feat: add a component that excludes its children from ssr

* feat: allow opting out of ssr based on request context
  • Loading branch information
futursolo authored Dec 12, 2022
1 parent 2171b04 commit 398b3f0
Show file tree
Hide file tree
Showing 9 changed files with 101 additions and 35 deletions.
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>
</>
}
}

0 comments on commit 398b3f0

Please sign in to comment.