I am creating a React application in which I have one state called input which is taking input from user. I want that when I press enter key, alert should display the input which is getting set in state. However, on clicking enter key, only default value set in input state is getting displayed.
But when I click on a button (which I have created to display the input in alert), then the input displayed in alert is correct.
Please see the code snippet below for reference:
import React, { useEffect, useRef, useState } from 'react';
export default function ShowAlertExample() {
const [input, setInput ] = useState('1');
const handleInputChange = (e) => {
setInput(e.target.value);
}
const handleShowAlert = () => {
alert(input);
}
const checkKeyPress = (e) =>{
const { key, keyCode } = e;
console.log(key, keyCode)
if (keyCode === 13 ) {
alert(input);
}
}
useEffect(()=>{
window.addEventListener("keydown", checkKeyPress)
return ()=>{
window.removeEventListener("keydown", checkKeyPress)
}
}, [])
return (
<div>
<header className="App-header">
<input value={input} onChange={handleInputChange} />
<button onClick={handleShowAlert}>Show Alert</button>
</header>
</div>
);
}
I made one change which can be seen in the code snippet below. I removed the []
from dependency list of useEffect()
and it started working fine, but now the issue is it is adding and removing event listener each time I add something to input. This is not a good practice I guess if we are creating a big application.
import React, { useEffect, useRef, useState } from 'react';
export default function ShowAlertExample() {
const [input, setInput ] = useState('1');
const handleInputChange = (e) => {
setInput(e.target.value);
}
const handleShowAlert = () => {
alert(input);
}
const checkKeyPress = (e) =>{
const { key, keyCode } = e;
console.log(key, keyCode)
if (keyCode === 13 ) {
alert(input);
}
}
useEffect(()=>{
window.addEventListener("keydown", checkKeyPress)
console.log("event listener added")
return ()=>{
console.log("event listener removed")
window.removeEventListener("keydown", checkKeyPress)
}
})
return (
<div>
<header className="App-header">
<input value={input} onChange={handleInputChange} />
<button onClick={handleShowAlert}>Show Alert</button>
</header>
</div>
);
}
Is there anyway I can add event listener with the desired functionality without causing any performance issue. That is when I press Enter key, the alert should display the correct input. I have referred to the below Stack Overflow post and tried using useCallback()
but it didn't work out.
:~:text=hooks%20to%20below-,useEffect(()%20%3D%3E%20%7B%20window.,the%20effect%20once%2C%20imitating%20componentDidMount%20.
I am creating a React application in which I have one state called input which is taking input from user. I want that when I press enter key, alert should display the input which is getting set in state. However, on clicking enter key, only default value set in input state is getting displayed.
But when I click on a button (which I have created to display the input in alert), then the input displayed in alert is correct.
Please see the code snippet below for reference:
import React, { useEffect, useRef, useState } from 'react';
export default function ShowAlertExample() {
const [input, setInput ] = useState('1');
const handleInputChange = (e) => {
setInput(e.target.value);
}
const handleShowAlert = () => {
alert(input);
}
const checkKeyPress = (e) =>{
const { key, keyCode } = e;
console.log(key, keyCode)
if (keyCode === 13 ) {
alert(input);
}
}
useEffect(()=>{
window.addEventListener("keydown", checkKeyPress)
return ()=>{
window.removeEventListener("keydown", checkKeyPress)
}
}, [])
return (
<div>
<header className="App-header">
<input value={input} onChange={handleInputChange} />
<button onClick={handleShowAlert}>Show Alert</button>
</header>
</div>
);
}
I made one change which can be seen in the code snippet below. I removed the []
from dependency list of useEffect()
and it started working fine, but now the issue is it is adding and removing event listener each time I add something to input. This is not a good practice I guess if we are creating a big application.
import React, { useEffect, useRef, useState } from 'react';
export default function ShowAlertExample() {
const [input, setInput ] = useState('1');
const handleInputChange = (e) => {
setInput(e.target.value);
}
const handleShowAlert = () => {
alert(input);
}
const checkKeyPress = (e) =>{
const { key, keyCode } = e;
console.log(key, keyCode)
if (keyCode === 13 ) {
alert(input);
}
}
useEffect(()=>{
window.addEventListener("keydown", checkKeyPress)
console.log("event listener added")
return ()=>{
console.log("event listener removed")
window.removeEventListener("keydown", checkKeyPress)
}
})
return (
<div>
<header className="App-header">
<input value={input} onChange={handleInputChange} />
<button onClick={handleShowAlert}>Show Alert</button>
</header>
</div>
);
}
Is there anyway I can add event listener with the desired functionality without causing any performance issue. That is when I press Enter key, the alert should display the correct input. I have referred to the below Stack Overflow post and tried using useCallback()
but it didn't work out.
https://stackoverflow.com/questions/55565444/how-to-register-event-with-useeffect-hooks#:~:text=hooks%20to%20below-,useEffect(()%20%3D%3E%20%7B%20window.,the%20effect%20once%2C%20imitating%20componentDidMount%20.
Share Improve this question edited Feb 15, 2021 at 19:37 Brian Tompsett - 汤莱恩 5,88372 gold badges61 silver badges133 bronze badges asked Feb 15, 2021 at 18:44 satyamsatyam 811 gold badge1 silver badge4 bronze badges 5 |2 Answers
Reset to default 14Since you always require the latest state value of input
, you will have to add checkKeyPress
to your useEffect
's dependency list and checkKeyPress
should itself have input
in the wrapper useCallback
deps list.
const checkKeyPress = useCallback((e) => {
const { key, keyCode } = e;
console.log(key, keyCode);
if (keyCode === 13) {
alert(input);
}
},[input]);
useEffect(() => {
window.addEventListener("keydown", checkKeyPress);
return () => {
window.removeEventListener("keydown", checkKeyPress);
};
}, [checkKeyPress]);
In your older code ,an empty array in useEffect
means that you're adding an event listener to the checkKeyPress
that was available after first render. Now that checkKeyPress
closes over the default value of input
.
You need to ensure that every time that input
updates, latest checkKeyPress
(one which closes over the latest input
) is added as an event handler and the old one removed.
Issue
You've enclosed the initial input
state value in the checkKeyPress
callback for the "keydown" event. Each time the callback is invoked you are accessing a stale state enclosure.
Solution
Instead of trying to use a global keyDown handler just attach an event handler directly to the elements you want to a callback to respond to, which is the standard way to handle DOM interactions in React anyway. This removes the stale enclosure and makes it a standard callback.
function ShowAlertExample() {
const [input, setInput] = useState("1");
const handleInputChange = (e) => {
setInput(e.target.value);
};
const handleShowAlert = () => {
alert(input);
};
const checkKeyPress = (e) => {
const { key, keyCode } = e;
console.log(key, keyCode);
if (keyCode === 13) {
alert(input);
}
};
return (
<div>
<header className="App-header">
<input
value={input}
onChange={handleInputChange}
onKeyDown={checkKeyPress} // <-- attach event handler
/>
<button onClick={handleShowAlert}>Show Alert</button>
</header>
</div>
);
}
checkKeyPress
function? Perhaps if you explained your use-case we could suggest a more optimal solution. I.e. It is usually better to just attach anonKeyDown
handler to the inputs that care about that event. This removes the stale enclosure issue. See codesandbox demo – Drew Reese Commented Feb 15, 2021 at 19:22onSubmit
handler to access the form fields. If you store the form field values in component state then you'll always have the issue of needing to re-enclose current state values in any callbacks. The alternative to state is to use a React ref per input/field and access the current value when you need to. – Drew Reese Commented Feb 15, 2021 at 19:52useEffect
without dependency will remove and re-add the event listener and callback on every render of the component, versus only when the dependency updates. While it may not be an issue with resetting the handler it's still unnecessary to do so on every render. – Drew Reese Commented Feb 15, 2021 at 20:07