docs: how not to mutate the DOM during rendering (#1344)

This commit is contained in:
Greg Johnston 2023-07-13 16:57:07 -04:00 committed by GitHub
parent 3481a6ee53
commit 193aa79956
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 79 additions and 45 deletions

View File

@ -74,11 +74,11 @@ In other words, if this is being compiled to WASM, it has three items; otherwise
When I load the page in the browser, I see nothing. If I open the console I see a bunch of warnings:
```
element with id 0-0-1 not found, ignoring it for hydration
element with id 0-0-2 not found, ignoring it for hydration
element with id 0-0-3 not found, ignoring it for hydration
component with id _0-0-4c not found, ignoring it for hydration
component with id _0-0-4o not found, ignoring it for hydration
element with id 0-3 not found, ignoring it for hydration
element with id 0-4 not found, ignoring it for hydration
element with id 0-5 not found, ignoring it for hydration
component with id _0-6c not found, ignoring it for hydration
component with id _0-6o not found, ignoring it for hydration
```
The WASM version of your app, running in the browser, expects to find three items; but the HTML has none.
@ -87,6 +87,56 @@ The WASM version of your app, running in the browser, expects to find three item
Its pretty rare that you do this intentionally, but it could happen from somehow running different logic on the server and in the browser. If youre seeing warnings like this and you dont think its your fault, its much more likely that its a bug with `<Suspense/>` or something. Feel free to go ahead and open an [issue](https://github.com/leptos-rs/leptos/issues) or [discussion](https://github.com/leptos-rs/leptos/discussions) on GitHub for help.
### Mutating the DOM during rendering
This is a slightly more common way to create a client/server mismatch: updating a signal _during rendering_ in a way that mutates the view.
```rust
#[component]
pub fn App(cx: Scope) -> impl IntoView {
let (loaded, set_loaded) = create_signal(cx, false);
// create_effect only runs on the client
create_effect(cx, move |_| {
// do something like reading from localStorage
set_loaded(true);
});
move || {
if loaded() {
view! { cx, <p>"Hello, world!"</p> }.into_any()
} else {
view! { cx, <div class="loading">"Loading..."</div> }.into_any()
}
}
}
```
This one gives us the scary panic
```
panicked at 'assertion failed: `(left == right)`
left: `"DIV"`,
right: `"P"`: SSR and CSR elements have the same hydration key but different node kinds.
```
And a handy link to this page!
The problem here is that `create_effect` runs **immediately** and **synchronously**, but only in the browser. As a result, on the server, `loaded` is false, and a `<div>` is rendered. But on the browser, by the time the view is being rendered, `loaded` has already been set to `true`, and the browser is expecting to find a `<p>`.
#### Solution
You can simply tell the effect to wait a tick before updating the signal, by using something like `request_animation_frame`, which will set a short timeout and then update the signal before the next frame.
```rust
create_effect(cx, move |_| {
// do something like reading from localStorage
request_animation_frame(move || set_loaded(true));
});
```
This allows the browser to hydrate with the correct, matching state (`loaded` is `false` when it reaches the view), then immediately update it to `true` once hydration is complete.
### Not all client code can run on the server
Imagine you happily import a dependency like `gloo-net` that youve been used to using to make requests in the browser, and use it in a `create_resource` in a server-rendered app.

View File

@ -204,11 +204,9 @@ impl Custom {
assert_eq!(
el.node_name().to_ascii_uppercase(),
name.to_ascii_uppercase(),
"SSR and CSR elements have the same `TopoId` but \
different node kinds. This is either a discrepancy \
between SSR and CSR rendering
logic, which is considered a bug, or it can also be a \
leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("id").unwrap();
@ -221,11 +219,9 @@ impl Custom {
assert_eq!(
el.node_name().to_ascii_uppercase(),
name.to_ascii_uppercase(),
"SSR and CSR elements have the same `TopoId` but \
different node kinds. This is either a discrepancy \
between SSR and CSR rendering
logic, which is considered a bug, or it can also be a \
leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("leptos-hk").unwrap();
@ -1270,11 +1266,9 @@ fn create_leptos_element(
assert_eq!(
&el.node_name().to_ascii_uppercase(),
tag,
"SSR and CSR elements have the same `TopoId` but different \
node kinds. This is either a discrepancy between SSR and CSR \
rendering
logic, which is considered a bug, or it can also be a leptos \
hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("id").unwrap();
@ -1287,11 +1281,9 @@ fn create_leptos_element(
assert_eq!(
el.node_name().to_ascii_uppercase(),
tag,
"SSR and CSR elements have the same `TopoId` but different \
node kinds. This is either a discrepancy between SSR and CSR \
rendering
logic, which is considered a bug, or it can also be a leptos \
hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("leptos-hk").unwrap();

View File

@ -67,11 +67,9 @@ macro_rules! generate_math_tags {
assert_eq!(
el.node_name().to_ascii_uppercase(),
stringify!([<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]),
"SSR and CSR elements have the same `TopoId` \
but different node kinds. This is either a \
discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it \
can also be a leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("id").unwrap();
@ -84,11 +82,9 @@ macro_rules! generate_math_tags {
assert_eq!(
el.node_name().to_ascii_uppercase(),
stringify!([<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]),
"SSR and CSR elements have the same `TopoId` \
but different node kinds. This is either a \
discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it \
can also be a leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("leptos-hk").unwrap();

View File

@ -64,11 +64,9 @@ macro_rules! generate_svg_tags {
assert_eq!(
el.node_name().to_ascii_uppercase(),
stringify!([<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]),
"SSR and CSR elements have the same `TopoId` \
but different node kinds. This is either a \
discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it \
can also be a leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("id").unwrap();
@ -81,11 +79,9 @@ macro_rules! generate_svg_tags {
assert_eq!(
el.node_name().to_ascii_uppercase(),
stringify!([<$tag:upper $(_ $second:upper $(_ $third:upper)?)?>]),
"SSR and CSR elements have the same `TopoId` \
but different node kinds. This is either a \
discrepancy between SSR and CSR rendering
logic, which is considered a bug, or it \
can also be a leptos hydration issue."
"SSR and CSR elements have the same hydration key but \
different node kinds. Check out the docs for information \
about this kind of hydration bug: https://leptos-rs.github.io/leptos/ssr/24_hydration_bugs.html"
);
el.remove_attribute("leptos-hk").unwrap();