Home > Enterprise >  State inside useEffect changing with delay
State inside useEffect changing with delay

Time:12-17

I'm trying to manipulate the DOM based on what's being typed (text) in an input in a React component:

import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";

function App() {
  const [text, setText] = useState("Initial value");

  function handleChange(event) {
    setText(event.target.value);
  }

  useEffect(() => {
    function handleKeypress(event) {
      console.log("text", text);
      // do something with the DOM with text
    }

    document.addEventListener("keydown", handleKeypress);

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

  return (
    <div id="keys">
      <input type="text" onChange={handleChange} value={text} />
    </div>
  );
}

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

It works, but something strange happens. text won't change unless keydown is triggered a second time.

For example, if you press a, console.log("text", text) will log Initial value. It won't log a. I think there's some kind of delay.

Why is this? And how to change the code so that console.log("text", text) logs the key that's being pressed?

Live code: https://codesandbox.io/s/react-hooks-useeffect-forked-sfujxi?file=/src/index.js

Note: The reason I'm using the event listener is that I want the code to also run when, for example, I press Ctrl Key (in that situation text doesn't change).

CodePudding user response:

It seems that this is because the text state is updated in the next render of the component, but the event block is still using the old value before the update.

Try not to use a event listener but let useEffect capture the change of the text state and it should log the correct input (when text changes):

  useEffect(() => {
    console.log("text", text);
  }, [text]);

Alternatively if the goal is to log the key pressed, perhaps consider to just capture it with event since the event will have the value of the pressed key.

There is no need to read from text state for this approach, instead perhaps consider to set the captured value as a state for display in some other components.

  useEffect(() => {
    function handleKeypress(event) {
      console.log(event.keyCode);
    }
    document.addEventListener("keydown", handleKeypress);
    return () => {
      document.removeEventListener("keydown", handleKeypress);
    };
  }, []);

CodePudding user response:

Considering the Note that you have provided in your question If you want the handleKeypress function to run when the user presses a key combination, such as Ctrl Key, you can use the event.ctrlKey property to check if the Ctrl key is being pressed.

function handleKeypress(event) {
  if (event.ctrlKey) {
    console.log("Ctrl   Key was pressed");
  }
}

If you want to check for specific key combinations for example, to check for Ctrl S you can do that using event.key property.

function handleKeypress(event) {
  if (event.ctrlKey && event.key === "s") {
    console.log("Ctrl   S was pressed");
  }
}
  • Related