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");
}
}