Home > Mobile >  Preventing loop inside useEffect with setState
Preventing loop inside useEffect with setState

Time:06-01

I want to fetch data when a state value changed.

This is easy approach since I can use useEffect and tell it to run when the state value changes. something like this

useEffect(() => {
  fetch(url,(result) => {
     // this is callback function when data fetched

  });

},[value]);

and somewhere in my render, I call setValue after the user clicks a button.

But in my situation, value has no default value so it's undefined the first time, My API provides the initial value for value. So the first time the useEffect must set value too.

useEffect(() => {
  fetch(url,({value, ...data}) => {
     // this is callback function when data fetched
     setData(data);
     setValue(value); // <- this line make loop since value is an object
    // But I need to set value for initial value that came from server
  });

},[value]);

And this makes an infinite loop since I'm changing value on effect which depends on value.

In short

1-I get my initial value from the API call

2-I need to re-call the API after the value is changed by user click.

These 2 condition makes an infinite loop, How I can approach this?

UPDATED

The value is an object that contains a year and month and its initial value is based on the user config in the database. It's something like {month: 10, year: 2022} and the user can change it in UI to navigate to other months.

CodePudding user response:

I you want to fetch something when value changes, I'd rather call it once, when user changes the value object.

function fetchValue(oldValue) {
  fetch(url, ({ value, ...data }) => {
    setData(data)
    setValue(value)
  })
}

useEffect(() => {
  // initial call to the API
  fetchValue()
}, [])

// Later...
<button onClick={() => {
  const newValue = updateValue(value)
  setValue(newValue)
  fetchValue(newValue)
}} />

or if you want to centralise effect handling with useEffect, you can split user-local value from what is returned from the server:

const [userValue, setUserValue] = useState()
const [serverValue, setServerValue] = useState()

useEffect(() => {
  fetch(url, ({ value, ...data }) => {
    setServerValue(value)
    setData(data)
  })
}, [userValue])

const actualValue = serverValue || userValue

<button onClick={() => {
  setServerValue(undefined)
  setUserValue(updateValue(actualValue))
}} />

CodePudding user response:

You can check if the value is `undefined`. If it's not you can set its value.

Something like this:

useEffect(() => {
  fetch(url,({fetchedvalue, ...data}) => {
     // this is callback function when data fetched
     setData(data);
     if (fetchedvalue == undefined){
          setValue(fetchedvalue); // <- this line make loop since value is an object
          // But I need to set value for initial value that came from server
     }
  });

},[value]);
  • Related