简体   繁体   中英

React Hooks useState vs useEffect endless loop

If I have the following code

function MyApp() {

    const [state, setState] = useState({name:'', updatedField:''});

    useEffect(() => {
        save(); //<---- endless loop here
    }, [state]);

    const save = async () => {
        var response = await api.save(state);

        var newState = {...state};
        newState.updatedField = response.updatedField;       

        setState(newState);
    }

    const onSaveBtnClicked = (event) => {
         var newState = {...state};
         newState.name = event.target.value;
         setState(newState);
    }
}

The object 'state' is being saved via my api when it is changed, but the act of saving to the api requires updating the 'state' object. This obviously creates an endless loop.

What is the best practice here considering I have no way of providing a callback when I update the state using setState?

I'd separate the updatedField out of the state and put it into a different variable:

function MyApp() {
    const [state, setState] = useState({name:'', /* include other fields here */});
    const [updatedField, setUpdatedField] = useState('');

    useEffect(save, [state]); // don't pass `state` to `save`
                              // since `save` doesn't take any parameters

    const save = async () => {
        var response = await api.save(state);
        setUpdatedField(response.updatedField);
    }

Then, wherever you're referring to state.updatedField , instead refer to the standalone updatedField identifier.

(make sure to catch possible asynchronous errors too - you don't want possible unhandled rejections)

I think I cam see how the loop happens:

  • you call setState
  • which then triggers the useEffect callback
  • which calls save
  • and save at the end calls setState
  • which then triggers the useEffect callback
  • which then calls save
  • which calls setState
  • which then triggers the useEffect callback...

I'm not sure why you need to call setState at the end of save. Does the api call in save change the state?

This is the solution I went for in the end.

function MyApp() {

    const [state, setState] = useState(
        {
            needsSaving:false,
            obj :{
                name:'', 
                updatedField:''
            }
        });

    useEffect(() => {

        if(state.needsUpdate)
            save(); 

    }, [state.needsSaving]);

    const save = async () => {
        var response = await api.save(state.obj);
        var newState = {...state};
        newState.obj.updatedField = response.updatedField;       
        newState.needsSaving = false;
        setState(newState);
    }

    const onSaveBtnClicked = (event) => {
        var newState = {...state};
        newState.obj.name = event.target.value;
        newState.needsSaving = true;
        setState(newState);
   }
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM