React.useState does not reload state from props

ReactjsReact Hooks

Reactjs Problem Overview


I'm expecting state to reload on props change, but this does not work and user variable is not updated on next useState call, what is wrong?

function Avatar(props) {
  const [user, setUser] = React.useState({...props.user});
  return user.avatar ? 
         (<img src={user.avatar}/>)
        : (<p>Loading...</p>);
}

codepen

Reactjs Solutions


Solution 1 - Reactjs

The argument passed to useState is the initial state much like setting state in constructor for a class component and isn't used to update the state on re-render

If you want to update state on prop change, make use of useEffect hook

function Avatar(props) {
  const [user, setUser] = React.useState({...props.user});

  React.useEffect(() => {
      setUser(props.user);
  }, [props.user])

  return user.avatar ? 
         (<img src={user.avatar}/>)
        : (<p>Loading...</p>);
}

Working demo

Solution 2 - Reactjs

Functional components where we use useState to set initial values to our variable, if we pass initial value through props, it will always set same initial value until you don't make use of useEffect hook,

for example your case this will do your job

 React.useEffect(() => {
      setUser(props.user);
  }, [props.user])

The function passed to useEffect will run after the render is committed to the screen.

By default, effects run after every completed render, but you can choose to fire them only when certain values have changed.

React.useEffect(FunctionYouWantToRunAfterEveryRender)

if you pass only one argument to useEffect it will run this method after every render you can decide when to fire this FunctionYouWantToRunAfterEveryRender by passing second argument to useEffect

React.useEffect(FunctionYouWantToRunAfterEveryRender, [props.user])

as you notice i am passing [props.user] now useEffect will only fire this FunctionYouWantToRunAfterEveryRender function when props.user is changed

i hope this helps your understanding let me know if any improvements are required thanks

Solution 3 - Reactjs

you can create your own custom simple hooks for this. The hooks changed default value when it changed.

https://gist.github.com/mahmut-gundogdu/193ad830be31807ee4e232a05aeec1d8

    import {useEffect, useState} from 'react';

    export function useStateWithDep(defaultValue: any) {
      const [value, setValue] = useState(defaultValue);
    
      useEffect(() => {
        setValue(defaultValue);
      }, [defaultValue]);
      return [value, setValue];
    }

#Example

const [user, setUser] = useStateWithDep(props.user);

Solution 4 - Reactjs

I've created custom hooks like this:

const usePrevious = value => {
   const ref = React.useRef();

   React.useEffect(() => {
       ref.current = value;
   }, [value]);

   return ref.current;
}

const usePropState = datas => {
    const [dataset, setDataset] = useState(datas);
    const prevDatas = usePrevious(datas);

    const handleChangeDataset = data => setDataset(data);

    React.useEffect(() => {
        if (!deepEqual(datas, prevDatas)) // deepEqual is my function to compare object/array using deep-equal
            setDataset(datas);
    }, [datas, prevDatas]);

    return [
        dataset,
        handleChangeDataset
    ]
}

To use:

const [state, setState] = usePropState(props.datas);

Solution 5 - Reactjs

The parameter passed to React.useState() is only the initial value for that state. React isn't going to recognize that as changing the state, only setting its default value. You'll want to set the default state initially, and then conditionally call setUser(newValue), which will be recognized as a new state, and re-render the component.

I would recommend caution updating state without some kind of condition to keep it from constantly updating, and therefore re-rendering everytime props are received. You may want to consider hoisting the state functionality to a parent component and passing the state of the parent down to this Avatar as a prop.

Solution 6 - Reactjs

According to the ReactJS documentation about Hooks :

> But what happens if the friend prop changes while the component is on the screen? Our component would continue displaying the online status of a different friend. This is a bug. We would also cause a memory leak or crash when unmounting since the unsubscribe call would use the wrong friend ID.

Your only interaction here should happen on a props change, which seems not to work. You could (still according to the doc) use a componentDidUpdate(prevProps) to proactively catch any update to the props.

PS : I don't have enough code to judge, but couldn't you actively setUser() inside your Avatar(props) function ?

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionvitalysterView Question on Stackoverflow
Solution 1 - ReactjsShubham KhatriView Answer on Stackoverflow
Solution 2 - ReactjsHanzla HabibView Answer on Stackoverflow
Solution 3 - ReactjsMahmut GundogduView Answer on Stackoverflow
Solution 4 - ReactjsOanView Answer on Stackoverflow
Solution 5 - ReactjsDrowsyView Answer on Stackoverflow
Solution 6 - ReactjsMwakView Answer on Stackoverflow