Home > Back-end >  Why does this React's useEffect() fires twice after loaded?
Why does this React's useEffect() fires twice after loaded?

Time:12-24

I have this very simple component called Editor:

export default function Editor() {
  const [error, setError] = useState(null);
  const [isLoaded, setIsLoaded] = useState(false);
  const [currentData, setCurrentData] = useState({});

  useEffect(() => {
    fetch("http://localhost:4000/pages")
      .then(res => res.json())
      .then(
        (result) => {
          setIsLoaded(true);
          setCurrentData(result);
        },
        (error) => {
          setIsLoaded(true);
          setError(error);
        }
      )
  }, [])

  if (error) {
    return (
      <LoadingError value={error.message} />
    );
  } else if (!isLoaded) {
    return (
      <Loading />
    )
  } else {
    console.log(currentData);
  }

  return ( <div></div> );
}

Notice the last else condition, where my currentData is logged into the console.

What happens when this component is rendered is that my console.log() fires twice! The first time my currentData object is empty. The second time, the object properly contains all the data from my API.

However, as my console.log() should fire only when there is no error (the first if condition), and only when the data is loaded (the second else if condition), how is it possible that I end up with empty object in the console the first time? What am I doing wrong?

CodePudding user response:

Your useEffect only fires once. You're seeing the console.log output twice because you're doing updateState twice. Change the order to see the expected behaviour you're looking for

This is what's happening right now:

setIsLoaded(true); // Causes 1st rerender and so console.log executes with []
setCurrentData(result); // Causes second rerender and console.log has data.

What you want:

setCurrentData(result); // Causes 1st rerender but does not reach the else statement
setIsLoaded(true); // Reaches the else statement and currentData is already set

This will make sure your currentData is set before isLoaded is set to true

As of now, React does not batch update state calls in the above scenario, but this will change once React v18 is released.

CodePudding user response:

Why does this React's useEffect() fires twice after loaded?

It doesn't.

The function Editor runs again because you call setIsLoaded and setCurrentData once the promise in the effect (which runs only once) resolves.

CodePudding user response:

I have a feeling that React only batches state updates for certain handlers, like onClick and others, but I have feeling that when state updates occur in the callback of a promise, they dont get batched, so you will have two renders happening in your case.

edit - which is probably what @Harkunwar's answer is alluding to, in that the order in which you update your state matters

  • Related