docs: how not to mutate the DOM during rendering (#1344)
This commit is contained in:
parent
3481a6ee53
commit
193aa79956
|
@ -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
|
|||
|
||||
It’s pretty rare that you do this intentionally, but it could happen from somehow running different logic on the server and in the browser. If you’re seeing warnings like this and you don’t think it’s your fault, it’s much more likely that it’s 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 you’ve been used to using to make requests in the browser, and use it in a `create_resource` in a server-rendered app.
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
Loading…
Reference in New Issue