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.
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;