What is a scalable way to retain state of a child component when the parent changes?

Viewed 24

I have a design where a stateful GrandParent renders either ParentOne or ParentTwo, but both parents renders the same stateful Child component. When the parent component is changed, I want the state of Child to be retained (and its possibly stateful children).

I have created a tiny codepen to show what I mean, and I will also include the JavaScript for future reference in case it disappears:

https://codepen.io/chwallen/pen/WNJRKVB

function Child() {
  const [myState, setMyState] = React.useState(false);

  return (
    <div>
      <p>Is state modified: {myState.toString()}</p>
      <button onClick={() => setMyState(true)}>Modify state</button>
    </div>
  );
}

function ParentOne({ children }) {
  return (
    <div className="parent-one">{children}</div>
  );
}

function ParentTwo({ children }) {
  return (
    <div className="parent-two">{children}</div>
  );
}

function GrandParent() {
  const [isTrue, setIsTrue] = React.useState(true);

  const Parent = isTrue ? ParentOne : ParentTwo;

  return (
    <div className="grand-parent">
      <button onClick={() => setIsTrue(!isTrue)}>Toggle parent</button>
      <Parent>
        <Child />
      </Parent>
    </div>
  );
}

ReactDOM.render(<GrandParent />, document.querySelector("#root"));

In this very simplified case, the solution is to move the Child state into the GrandParent. However, in the real case, there are more than one Child component which is dependent on a second state in GrandParent. Each of the child components may be stateful, and they in turn may contain stateful children. Moving all of that state to the GrandParent is not scalable. Additionally, I will lose the automagical state reset when the child, not parent, is unmounted (which is desirable) if the state is present in the GrandParent.

A second option would be to move the rendering of the Parent component as far down in the component tree as possible, i.e., each child would render the parent individually. This goes against DRY, and is really only moves the problem from GrandParent to the children.

One note: in this example, ParentOne and ParentTwo could easily be merged and controlled via props. In the real-world application however, these two components are vastly different and cannot be merged.

So my question is:

How can I, in a scalable way, retain the state for an arbitrary number of stateful children when their parent changes?

1 Answers

Store state in the GrandParent component, then pass it down or use context. I would put a context provider in the GrandParent, containing state and memoized control methods, and have a custom useMyContext type hook that I can use in the child components to reference/control state changes.

Related