I'm working with React and TypeScript. I'm new to React and I'm not sure how to ensure that a child component sets state properly based on a prop passed to it.
I am passing an object from a parent component into a child component as a prop. The parent component has an Object with keys and empty values. An example of this is below -
emptyObject = {
id: '',
name: '',
timestamp: ''
}
An async function foo
queries a database and populates the values based on the component's prop, then returns a Promise of the updated Object. The updated Object should then be passed to the child component, where values are stored as State.
function ParentComponent({propName}) {
const [myObject, setMyObject] = useState(emptyObject);
useEffect( () => {
foo(emptyObject).then((x) => {
setMyObject(x);
});
}, [propName]);
return (
<>
<ChildComponent obj = {myObject} />
</>
);
}
function ChildComponent({obj}) {
const {id, name, timestamp} = obj;
console.log("id: " + id + ". name: " + name) // this gives the correct values
const [myId, setMyId] = useState(id)
const [myName, setMyName] = useState(name);
console.log("id: " + myId + ". name: " + myName) // this gives "id: undefined. name: undefined"
return (
<displays a form that is pre-populated with the id and name values>
)
}
The issue I face is that destructuring the Object gives me the correct values, but the values in State are undefined. I suspect this is because setting State in React is asynchronous, but I'm not sure how to address this.
The first thing I tried was adding a hook in the child Component, but the same issue occurred.
function ChildComponent({obj}) {
const {id, name, timestamp} = obj;
const [myId, setMyId] = useState(id)
const [myName, setMyName] = useState(name);
useEffect( () => {
setMyId(id);
setMyName(name);
}, [obj]);
I also tried implementing two useEffects
in the parent and modifying the logic in the original useEffect
to ensure that values updated but the same result still happens.
const [myObject, setMyObject] = useState(emptyObject);
const [updatedObject, setUpdatedObject] = useState(emptyObject);
useEffect( () => {
foo(emptyObject).then((x) => {
setUpdatedObject(x);
});
}, [propName]);
useEffect( () => {
setMyObject(updatedObject);
}, [updatedObject]);
return (
<>
<ChildComponent obj = {myObject} />
</>
);
the first time child
render, the prop passes on it equal to the init value you set (this is why you got undefined) until the foo
function set the prop. useState hook initialize only first and when the init value is updated the state isn't updated.
you can resolve this by listening for prop updates in child
like this.
function ChildComponent({obj}) {
const [myId, setMyId] = useState('')
const [myName, setMyName] = useState('');
useEffect(()=>{
setMyName(obj.name)
setMyId(obj.id)
},[obj]);
return (
<displays a form that is pre-populated with the id and name values>
)
}
my suggestion is to use the prop you passed into child
because it's already a state in parent
and any change in make rerender to you child
function ChildComponent({ obj, }) {
const myId = obj.id; // or use it directly `obj.id`
const myName = obj.name; // or use it directly `obj.name`
.....
}
if you need to update the prop from the child
pass the setter function as prop setMyObject
function ChildComponent({obj, setMyObject}) {....}