reactjsreact-hooks

React - useState not setting initial value


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}
/>

Solution

  • As it is alluded to in the comments, useState(initialState) (or whatever you call it) will only use initialState on the first render.

    The value you want the state to be initially. It can be a value of any type, but there is a special behavior for functions. This argument is ignored after the initial render.

    (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 (...)
    }
    

    Note that just setting state based on the props changing is a bit of an anti-pattern, as MonsterBucket notices you could just rely directly on the props changing to trigger a re-render:

    export const MakeComponent = props => {
      const { path, value, info, update } = props;
    
      const [val, setVal] = useState(value);
      if (val !== value) { // don't update unnecessarily
        setVal(value);
      }
    
     return (...)
    }
    

    And instead reserve useEffect for side effects, mostly those outside of the React render cycle.

    To see examples of these, have a look as these ReactJs docs - You might not need an effect, which covers lots of other examples.