Keydown event not working as same as onClick in React

Viewed 1178

I am trying to trigger a slider next and previous events on

  • next and previous click or
  • Left and Right Arrow Click

OnClick events are working as expected. But KeyDown handler not working as expected. It is working fine on first keypress, like incrementing or decrementing by 1. But after that, it either goes to -1 or +1.

I guess, I am missing some basic concept. Pls guide me where I am missing.

You can test here: https://codesandbox.io/embed/zen-goodall-35f17?fontsize=14&hidenavigation=1&theme=dark

import React, {useState, useEffect} from "react"

function App(){
  const [curImage, move] = useState(0);

  const handleKeypress = (e) => {
      console.log("keydown", e.keyCode);
      if (e.keyCode === 37) {
          leftClick();
      } else if (e.keyCode === 39) {
          rightClick();
      }
  };

  /* eslint-disable */
  useEffect(() => {
      document.addEventListener("keydown", handleKeypress);
      return () => document.removeEventListener("keydown", handleKeypress);
  }, []);
  /* eslint-enable */

  function leftClick() {
    move(curImage - 1);
}

function rightClick() {
    move(curImage + 1);
}

  return(
    <>
      <button onClick={leftClick}>prev</button>
      {"   "}
      {curImage}
      {"   "}
      <button onClick={rightClick}>next</button>
    </>
  )

}

export default App;
2 Answers

You can use functional state update to access the previous state and useCallback hook for memoization so that useEffect won't trigger unnecessarily.

Edit cocky-lake-pfg2l

import React, { useState, useEffect, useCallback } from "react";

function App() {
  const [curr, setCurr] = useState(0);

  const leftClick = useCallback(() => {
    setCurr((prevCurr) => prevCurr - 1);
  }, []);

  const rightClick = useCallback(() => {
    setCurr((prevCurr) => prevCurr + 1);
  }, []);

  const handleKeypress = useCallback(
    (e) => {
      if (e.keyCode === 37) {
        leftClick();
      } else if (e.keyCode === 39) {
        rightClick();
      }
    },
    [leftClick, rightClick]
  );

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

  return (
    <>
      <button onClick={leftClick}>prev</button>
      {"   "}
      {curr}
      {"   "}
      <button onClick={rightClick}>next</button>
    </>
  );
}

This is an issue with stale closure values. The solution of using useCallback hook provided by Ramesh should work for you.

Related