useState not getting set in React

Viewed 826

I'm trying to get useState to work when a user clicks up or down arrows and for some reason the state is not been updated.

Even if I setTimeout to see if index gets updated still not. Totally confused here with this.

import React, { useEffect, useState } from 'react';

const Wrapper = ({ children }) => {
  const [index, setIndex] = useState(-1);

  useEffect(() => {
    document.addEventListener('keydown', handler);
    return () => {
      document.removeEventListener('keydown', handler);
    };
  }, []);

  const handler = (event) => {
    if (event.key === 'ArrowDown') {
      setIndex(index + 1);
      console.log('index = ', index);
    }
  };

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

export default Wrapper;
2 Answers

Because of the async nature of setState you should log the previous state.

setIndex(prev => {
  console.log("prev = ", prev);
  return prev + 1;
});

Edit Q-56778259-LogPrevState

The hint is in const [index... - it will never change. Use functional form of setState:

const handler = (event) => {
  if (event.key === 'ArrowDown') {
    setIndex(oldIndex => oldIndex + 1)
  }
}

It's because the index during the first render will be -1, never change even in the closure inside the handle function. During a new render, a new handle function with different constant index will be created, but that function will be ignored because of useEffect(fn, []) second argument ensures that fn will be executed ONLY during first render.

See https://overreacted.io/a-complete-guide-to-useeffect/ for more details.

Related