Home > OS >  React renders previous state on state change
React renders previous state on state change

Time:11-10

This is a simplified version of my code, where I am having a problem.

 const Certificates = () => {
  const [currentList, setCurrentList] = useState(false);
  const [sort, setSort] = useState('posted');

  useEffect(() => {
    if (gameDetails) {
      setCurrentList({})
    }
  }, [gameDetails])

  useEffect(() => {
    if (sort === 'alphabetical') {
      setCurrentList([])
    }
  }, [sort])

  return (
    <>
      {
        currentList && sort === 'posted' && Object.keys(currentList)
      }
      {
        currentList && sort === 'alphabetical' && currentList.map
      }
    </>
  )
}

I have a state variable which changes from Object to Array depending on the active sort.

I have a conditional rendering where if the sort is 'posted' it must do the rendering considering the state variable is a Object. if the sort is 'alphabetical', it must do the rendering considering the state variable is a Array.

The problem is when the state variable changes from Object to Array, I am getting a currentList.map is not a function error.

When rendering react is considering currentList as a Object even though its changed to a Array.

The proof for the above statement is: I tried doing this:

<div>
    {
      currentList && console.log(currentList)
    }
  </div>

When the state variable changes to an Array this is the log I am getting:

-> {20/09/2022: Array(1), 18/01/2022: Array(1), 30/06/2021: Array(1), 24/06/2021: Array(5)}
-> [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]

When the state changes React first tries to render the previous state, that is where the error is occurring.

How can I solve this, any help will be appriciated.

CodePudding user response:

I'm not sure exactly what you're trying to achieve here, but I believe your problem is caused by stale closure occurring when use hooks, you can read up on it here: https://dmitripavlutin.com/react-hooks-stale-closures/.

In React state is only updated after the current execution context is finished - that's why it takes two re-renders to get the desired state.
I also had this issue some time ago, this question might help: React function takes two button clicks to run.

CodePudding user response:

Because you rely on useEffect to update currentList after sort has been updated, you will get one render where they are mismatched. Just make a function / functions to set them both at once.

function setToPosted(){
  setSort('posted');
  setCurrentList({});
}

function setToAlphabetical(){
  setSort('alphabetical');
  setCurrentList([]);
}

Alternatively you can just check if the value is an array or not

<>
      {
        currentList && !Array.isArray(currentList) && Object.keys(currentList)
      }
      {
        currentList && Array.isArray(currentList) && currentList.map
      }
</>
  • Related