0
votes

The child component passed to another component is losing value in its status

[environment]:

I have 5 components: Default, Wrapper, ChildrenComponent, Div1, Div2

In Default I'm rendering the Wrapper and inside the Wrapper I'm rendering ChildrenComponent, all of this in Default, I'm passing to Wrapper a "props" called "isDiv1", which is also my status in Default, I'm changing its value through a button in Default.

In Wrapper I am performing a logic analyzing if the value in "props.isDiv1" is true, if it is the Div1 component if not, I keep the Div2 Component, and I use the result of this logic to involve the "props.children" in Div1 or Div2 .

In ChildrenComponent I have a status that stores an initial text, and an input where every time it is changed, it changes this initial text to what has been changed.

And in Div1 and Div2 I have a simple div with a different "className" attribute.

import React from "react";

const Div1 = props => <div className="iam-div-1">{props.children}</div>;

const Div2 = props => <div className="iam-div-2">{props.children}</div>;

const Wrapper = props => {
  const Div = props.isDiv1 ? Div1 : Div2;

  return <Div>{props.children}</Div>;
};

const ChildrenComponent = props => {
  const [text, setText] = React.useState("initial");
  return <input value={text} onChange={e => setText(e.target.value)} />;
};

export default props => {
  const [isDiv1, setIsDiv1] = React.useState(false);

  return (
    <>
      <Wrapper isDiv1={isDiv1}>
        <ChildrenComponent />
      </Wrapper>
      <button onClick={() => setIsDiv1(!isDiv1)}>change div</button>
    </>
  );
};

[problem]:

The problem is that every time I change the status of ChildrenComponent and click the button in Default, the value in the status of ChildrenComponent is reset.

Below is the link for you to test yourselves:

https://codesandbox.io/s/headless-monad-ymc2i?file=/src/App.js

1

1 Answers

3
votes

The issue is that your Wrapper component is mounting a new Div component every time the value of isDiv1 changes. By mounting a new Div component, you are in turn mounting a new ChildComponent, which will always have the initial value.

A component will not stay mounted if it's parent becomes unmounted, nor will that component's props (i.e. React.Children) be preserved. If you think about the component tree like a real tree; a twig will not stay hanging in mid-air when the branch connecting it to the trunk is cut off.

If you want to preserve state when a component is unmounted, then you need to move that state into a higher level of the component tree. In your case it might look something like this:

const ChildrenComponent = props => {
  return <input value={props.text} onChange={e => props.setText(e.target.value)} />;
};

export default props => {
  const [isDiv1, setIsDiv1] = React.useState(false);
  const [text, setText] = React.useState("initial");

  return (
    <>
      <Wrapper isDiv1={isDiv1}>
        <ChildrenComponent text={text} setText={setText}/>
      </Wrapper>
      <button onClick={() => setIsDiv1(!isDiv1)}>change div</button>
    </>
  );
};