-1

I'm listening to keypress in a React component:

import * as React from "react";
import { render } from "react-dom";

function App() {
  const [keys, setKeys] = React.useState<string[]>([]);

  React.useEffect(() => {
    const handleKeypress = (event: any) => {
      if (event.key !== "]" && event.key !== "[" && event.key !== "Enter") {
        setKeys([...keys, event.key]);
        console.log(keys)
        // if you pressed, say, `d`, this should log `['d']`,
        // but it logs `[]` instead. 
      }

      // do something with keys
    };

    document.addEventListener("keypress", handleKeypress);

    return () => {
      document.removeEventListener("keypress", handleKeypress);
    };
  }, [keys]);

  return (
    <div>
      {keys.map((key: string, idx) => (
        <li key={idx}>{key}</li>
      ))}
    </div>
  );
}

const rootElement = document.getElementById("root");
render(<App />, rootElement);

Something strange happens. console.log(keys) will log an empty array the first time you press a key ... but that key will show in the JSX.

Why is this? And how to change the code so that console.log(keys) doesn't log an empty array the first time you press a key? I want to use keys as soon as it changes.

Live code: https://codesandbox.io/s/changing-props-on-react-root-component-forked-bjqsxe?file=/src/index.tsx

alexchenco
  • 53,565
  • 76
  • 241
  • 413

1 Answers1

2

useState by default simply does one thing, it sets the new state and cause a re-render to the function.

It is asynchronous in nature so by default, methods running after it usually run.

When you call setKeys, you trigger a re-render of your component, which will call again this function. However, the console.log(keys) inside the function still references the first value returned by useState, which is your original value (or the last one).

If you want to print it, you can do it by using the console.log outside of the useEffect.

Raz Levy
  • 138
  • 1
  • 9