Now, change the value of m
(TheDemo
‘s state), the structure of component tree changed, and the v
(TheChild
‘s state) will reset to init value 3
.
What I want is: while change the value of m
, keep the value of v
unchanged.
https://codesandbox.io/s/cool-waterfall-if1v0z?file=/index.js
import React, { StrictMode, useState } from "react";
import { createRoot } from "react-dom/client";
function TheDemo() {
const [m, setM] = useState(true);
const child = <TheChild />;
return (
<div style={{ fontFamily: "monospace" }}>
<button onClick={() => setM(!m)}>m = {+m}</button>
{m ? <b>{child}</b> : <span>{child}</span>}
</div>
);
}
function TheChild() {
const [v, setV] = useState(3);
return <button onClick={() => setV(v + 1)}>v = {v}</button>;
}
const root = createRoot(document.getElementById("root"));
root.render(
<StrictMode>
<TheDemo />
</StrictMode>
);
Maybe this a X-Y problem? The origin problem is I want to allow user to change page layout , move a child_0 from parent_1 to parent_2.
The demo is simplified, I have a lot of nested children components and lift up all state is nearly impossible.
Tao’s comment solved this question, and the react docs link here.
2
Answers
To keep the value of
v
unchanged while changing the value ofm
, you can lift the state ofTheChild
component up to theTheDemo
component and pass it down as props. Here’s the modified code:Now, when you change
m
, the value ofv
will remain unchanged because it’s managed by the parent componentTheDemo
. This way, you can also change the page layout while keeping the state of child components unchanged.As pointed out in Preserving and Resetting State, React associates a component’s state with its position in DOM and its HTML tag (or, in more general terms, with the DOM tree structure). If one of those changes, the state is reset in the changed branches 1. In your case, the position is kept, but the HTML tag is changed.
The simplest way to fix it (in this case) would be to keep the HTML
<span>
tag and change itsstyle
from{}
(ornull
) to{ fontWeight: 'bold' }
based onm
‘s truthiness:If you’re looking for a more generic mechanism (and this was just a simplified example), you have several other options to persist state in React:
m
changes and pass the state down through propsReact.useState()
withJotai.useAtom()
and you’re good to go 2.All state management solutions have a way to set a boundary on the context using that state (typically a Provider), so you can have multiple instances of the same state (e.g: in a list rendering example, where you’d want each list item and all of its children to use their own context).
1 – there is an exception: you can change the position in DOM without losing state by using
:key
(and keeping the same tag/component)2 – at least in the case you presented; more complex data sharing might require more complex solutions; I’d say Jotai is good at hiding its own complexity, but does have a few gotchas.