'How to update ancestor state from deeply nested child component without reducers
I have a react app with a structure similar to the code below (but more complex). In reality, all I am changing is the data in the grandchild component (grandchild.name in this example). However, I have to pass getters (props) and setters (onChange) handlers on all the components along the hierarchy, making it unnecessarily complicated (especially if more nested components are added (e.g. GreatGrandChild).
// Parent
const Parent = () => {
const [child, setChild] = useState({
name: 'Jack',
grandChild: {
name: 'John'
}
});
return (
<div>
<Child data={child} onChange={newChild => setChild(newChild)} />
</div>
);
};
// Child
const Child = props => {
const handleChange = newGrandChild => {
props.onChange({ name: props.data.name, grandChild: newGrandChild });
};
return (
<div>
<GrandChild data={props.data.grandChild} onChange={handleChange} />
</div>
);
};
// Grand Child
const GrandChild = props => {
const handleChange = e => {
props.onChange({ name: e.target.name });
};
return (
<div>
<input type='text' onChange={handleChange} />
</div>
);
};
So how can update the parent state without adding the handleChange function in the child component. I heard "function currying" can solve this, but I don't know how to use it in this case.
P.S. I need a solution that does not include Contexts and/or Reducers, as that would be overkill for my use-case. Also, creating a "updateGrandChild" function in parent and passing it to the grandchild component won't work either, as my data structure includes an array property, where the index is dynamic.
Solution 1:[1]
import { useState } from "react";
// Parent
const Parent = () => {
const [child, setChild] = useState({
name: "Jack",
grandChild: {
name: "John"
}
});
return (
<div>
<pre>{JSON.stringify(child, null, 2)}</pre>
<Child data={child} onChange={setChild} />
</div>
);
};
// Child
const Child = ({ data, onChange }) => {
return (
<div>
<GrandChild data={data.grandChild} onChange={onChange} />
</div>
);
};
// Grand Child
const GrandChild = ({ data, onChange }) => {
return (
<div>
<input
type="text"
value={data.name}
onChange={(e) =>
onChange((data) => ({
...data,
grandChild: {
...data.grandChild,
name: e.target.value
}
}))
}
/>
</div>
);
};
export default function App() {
return <Parent />;
}
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | Harsh Mangalam |
