Search code examples
reactjsrenderingstatereact-hooksrerender

When exactly do React Hook states get set?


I am trying to understand the order of execution for the following piece of code involving React Hook states:

const App = () => {
  const [ searchedCountry, setSearchedCountry ] = useState(''); 
  const [ filteredArr, setFilteredArr ]  = useState([]); 
  const [ filteredLength, setFilteredLength ] = useState(0);

  //...

  const filterCountries = (event) => { 
    event.preventDefault();

    setFilteredArr(countries.filter(country => country.name.includes(searchedCountry)));  
    setFilteredLength(filteredArr.length); 
    console.log("filtered arr length", filtered.length);
  }

  //...
}

When filterCountries is triggered, setFilteredArr sets the state, filteredArr, to an array filtered by a query. However, when exactly does filteredArr get set?

filteredArr.length returns 0, meaning filteredArr has not been set yet, even after calling setFilteredArr.

At first, I thought by executing setFilteredArr, the component re-renders, causing execution to skip the method calls after setFilteredArr. That would explain why filteredArr.length is 0. However, the console.log is still called, meaning after a component re-renders, the order of execution is actually resumed.

What is going on? When exactly does filteredArr get set?


Solution

  • The thing to remember is: all your state variables are local variables. They only exist for this particular time the component rendered. So on the first render, console.log("filtered arr length", filteredArr.length); is referring to the array that exists on that first render. filteredArr will never be assigned a new array (it can't, it's a const), and unless you mutate the array (which you shouldn't), the length of that array will always be 0.

    When you call setFilteredArr, this instructs react to rerender the component. React might do the rendering synchronously, or it might wait to try to batch up changes. When that 2nd render happens, you make a call to useState and get back the new value. This is assigned to a local variable named filteredArr, but this is a completely different variable than the one we had on the first render. The console.log statement in that first render, will have no way to access the variable in the second render. But the second render has access to it, so any logging you do the second time will show the second array.