I'm refactoring to use Hooks and I've hit a very confusing wall
I have a basic functional component like so:
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState(value);
console.log(value, val); // abc undefined
return (...)
}
The log returns abc undefined
- i.e. value
in props is definitely defined, but the first argument returned from useState(value)
is undefined
Just to test that hooks were working at all, I tried useState("abc")
and that logs abc
as expected.
I have no idea what I'm doing wrong - any ideas?
React version: 16.8.6
EDIT here is the parent component - nothing fancy going on here as far as I can see!
<MakeComponent
path={key}
value={item[key]}
info={value}
update={updateDiffs}
/>
To set a conditional initial value for useState in React: Pass a function to the useState hook. Use a condition to determine the correct initial value for the state variable. The function will only be invoked on the initial render.
The key difference is that the initial value of the state defined by useState can be anything you want it to be. It no longer has to be an object. A string, a number, an object, undefined , null - anything goes!
If you want to set an initial value for the variable, pass the initial value as an argument to the useState function. When React first runs your component, useState will return the two-element array as usual but will assign the initial value to the first element of the array, as shown in figure 5.
The answer: They're just queues setState , and React. useState create queues for React core to update the state object of a React component. So the process to update React state is asynchronous for performance reasons. That's why changes don't feel immediate.
As it is alluded to in the comments, useState(initialState)
(or whatever you call it) will only use initialState
on the first render.
During the initial render, the returned state (
state
) is the same as the value passed as the first argument (initialState
).
(React Docs, emphasis mine)
After this, on re-renders the useState function does not change the state based on new changes to the props passed in.
To make changes reflected everytime value
changes, register an effect on the input prop like so
export const MakeComponent = props => { const { path, value, info, update } = props; const [val, setVal] = useState(value); useEffect(() => { setVal(value)}, [value] ) return (...) }
Here you have to add useEffect
if you want to update the state on props change, which will listen to prop change & will update the state value accordingly
here is the updated snippet
export const MakeComponent = props => { const { path, value, info, update } = props; const [val, setVal] = useState(value); useEffect(() => { setVal(value)}, [value] ) return (<div>{val}</div>) }
Attching sandbox link
https://codesandbox.io/s/confident-agnesi-ohkq7?file=/src/MakeComponent.js
By the time you pass the prop value
to useState
the value of it can be yet to set. value
itself might have been undefined
yet.
Also setState
is not truly sync so if the useState
uses same mechanism as setState
your state val
might not be set to value
yet as the initial value.
In such cases, using props as the initial values to states, you should use a side effect with no dependency. Once the first render achieved the effect will run and set your state with prop. You can let the initial value of the component be undefined passing nothing with no problems.
export const MakeComponent = props => {
const { path, value, info, update } = props;
const [val, setVal] = useState();
// get initial state after initial render
useEffect(() => {
setVal(value)
}, [])
console.log(value, val); // abc undefined then will log abc abc
return (...)
}
Just keep in mind that props in React are meant to be read-only, state is for read and write. But it is perfectly fine, and no not an anti pattern, if you use a prop just as an initial value for a state and use the state you have set with the prop after that instead of the prop. That is for consistency since you might have two different values at a time from a prop and a state in circumstances.
Your case might need to care for the value of the prop at an arbitrary time depending on you needs as stressed in one of the above answers. You question does not state that. Still, if so, you can add the prop to the dependency array of the effect that sets the state with that prop, and yes write separate effects for other props you want the same, well, effect.
If you don't need writing for that data you do not need that pattern at all, just use prop and it will be up to date and you will have consistency. But you apparently do so I hope the pattern I suggest above works for you as it does for me.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With