so i have a wrapper component with a button which produce its children component whenever clicked no matter how many times. it is a wrapper component and i want to use it how many times i need but everytime i switch from one to another its state will reset. i switch in this piece of code: (in summary there are divs when you click on each one of them they show the respective handler)
import { useState } from "react";
import "./../styles/left.css";
import rightIcon from "./../assets/right.svg";
import General from "./general";
import Educational from "./educational";
import Professional from "./professional";
import Handler from "./handler";
export default function Left({ data, onChange }) {
const [selected, setSelected] = useState(0);
return (
<div className="forms">
<div className="form-type">
<div className="general-btn" onClick={() => setSelected(0)}>
general information{" "}
{selected === 0 ? <img src={rightIcon} alt="" /> : ""}{" "}
</div>
<div className="educational-btn" onClick={() => setSelected(1)}>
educational experience{" "}
{selected === 1 ? <img src={rightIcon} alt="" /> : ""}
</div>
<div className="practical-btn" onClick={() => setSelected(2)}>
practical experience{" "}
{selected === 2 ? <img src={rightIcon} alt="" /> : ""}
</div>
</div>
<div className="form">
{selected === 0 ? <General data={data} onChange={onChange} /> : ""}{" "}
{selected === 1 ? <Handler> <Educational data={data} onChange={onChange}/> </Handler> : ""}{" "}
{selected === 2 ? <Handler> <Professional data={data} onChange={onChange}/></Handler> : ""}{" "}
</div>
</div>
);
}
and it is my handler component:
import { useState } from "react";
export default function Handler({children}){
const [childrens, setChildrens] = useState([]);
const addChild = () => {
setChildrens(prevChildren => [...prevChildren, children]);
};
return(
<div>
{childrens}
<button onClick={addChild}>add</button>
</div>
)
}
i thought the states are independent in each component but i see otherwise. why?
also i tried to lift my state up from my handler component but this way i have to repeat myself and i hate doing it.
2
Answers
The part
is equivalent to
for all purposes – they render the same element tree. (I’ve ignored the empty/space strings, I don’t think they matter for React’s component reconciliation algorithm).
So when you switch between options
1
and2
, there’s still a<Handler>
element, and React renders the same component with its existing state. If that’s not what you expect, you should be able to fix this by giving the elements differentkey
s:You’ll probably have a similar problem later with the
childrens
insideHandler
– when you copy thechildren
into your array you’ll probably want to give it a unique id so that you can (re)move/reorder thechildrens
individually later; usecloneElement
for that.The
Handler
component state resets because when theselected
state updates theHandler
components are conditionally rendered; only the activeHandler
is mounted, the other are unmounted. WhenHandler
remounts it will start with the initial state.To address this you will need to lift the state up to a common ancestor component that remains mounted while these
Handler
components are conditionally rendered so their state can be "restored" when they remount.An example solution would be to store the children "counts" in state in the
Left
component.Update
Handler
to initialize thechildrens
state to the number of saved children count, and call theonAdd
callback to increment the count each time a new child is added.Demo