Home > Blockchain >  After a render of not showing a child, the child state data was gone completely when it render again
After a render of not showing a child, the child state data was gone completely when it render again

Time:08-12

In my parent component, there is a button to control whether to show the child or not.

In the input box, the input value is stored inside child component. When value is input, everything seem fine, data is recorded very fine in the child's state.

1. enter image description here

  1. click to not show it enter image description here

  2. click to show again enter image description here

Child's input box value was gone, and the state value in child was gone as well.

How can I keep the value when I click to show the child again?

In parent:

import React, { useEffect, useState } from "react";
import ReactDOM from "react-dom";
import Child1 from "./child";

import "./styles.css";

function Parent() {
  const [message, setMessage] = useState("initial text");
  const [showChild,setShowChild] = useState(true);
  useEffect(() => {
    console.log("useeffect in parent");
  });

  return (
    <div className="App">
      <button onClick={() => setShowChild(!showChild)}>show child</button>
      {showChild? 
      <Child1 />
      :
      null
      
    </div>
  );
}

const rootElement = document.getElementById("root");
ReactDOM.render(<Parent />, rootElement);

In child:

import React, { useEffect, useState } from "react";

function Child1() {
  useEffect(() => {
    console.log("useeffect in child");
    console.log("newMessage: "   newMessage);
  });
  const [newMessage, setNewMessage] = useState("");


  return (
    <div>
      <input onChange={(event) => setNewMessage(event.target.value)} />
    </div>
  );
}

export default Child1;

CodePudding user response:

This is because you are conditionally rendering <Child1 /> which means that when you click the toggle again to hide it, React basically unmounts it i.e. it is removed completely from the tree and thus all component state is lost. When you click the toggle again to show the component, it is mounted again.

One way to fix this would be to set show prop on the component itself and let it manage how it is shown and hidden. For example:

In parent:

  return (
    <div className="App">
      <button onClick={() => setShowChild(!showChild)}>show child</button>
      <Child1 show={showChild} />
    </div>
  );

In child:

import React, { useEffect, useState } from "react";

function Child1(props) {
  useEffect(() => {
    console.log("useeffect in child");
    console.log("newMessage: "   newMessage);
  });
  const [newMessage, setNewMessage] = useState("");

  if (!props.show) return <React.Fragment />

  return (
    <div>
      <input onChange={(event) => setNewMessage(event.target.value)} />
    </div>
  );
}

export default Child1;

Inside child, you may alternatively use the prop show to set CSS style display:none on div when show is false. This ways the input element itself with remain in the DOM but is simply hidden.

So instead of returning <React.Fragment /> we would modify the return statement as follows:

return (
  <div style={props.show ? null : {display: "none"}}>
    <input onChange={(event) => setNewMessage(event.target.value)} />
  </div>
);

As a third option, you can "elevate the state" meaning that you move newMessage state up to parent and pass newMessage and setNewMessage down to child as props, and keep the rest of the code how it is in your question.

In parent:

const [newMessage, setNewMessage] = useState("");
...
return (
  ...
  <Child1 newMessage={newMessage} setNewMessage={setNewMessage} />
  ...
)

In child:

const {newMessage, setNewMessage} = props;
  • Related