Reputation: 8136
I'm using the following component to submit comments in my app:
const App = () => {
const [text, setText] = React.useState("");
const send = React.useCallback(() => {
setText("");
console.log("sending", text);
}, [text]);
React.useEffect(() => {
const handler = e => {
switch (e.keyCode) {
case 13: // enter
if (e.shiftKey) {
e.preventDefault();
send();
}
break;
}
}
document.addEventListener("keydown", handler);
return () => document.removeEventListener("keydown", handler);
}, []);
return <div className="App">
<textarea
className="App__text"
value={text}
onChange={e => {
setText(e.target.value);
}} />
<button className="App__send" onClick={send}>send</button>
</div>;
};
It's a simple text field and button. When either the button, or shift-enter are pressed, the text in the text field is sent to the server (here we just console.log
it).
The button works fine - enter "hello world"
(or whatever) press the button, and the console will say hello world
.
Shift-enter, however, always prints an empty string.
I'm guessing I'm misunderstanding useCallback
. As I understand it, useCallback
wraps your function. When one of the dependencies change, React swaps out your function without changing the wrapper function so it's still an up-to-date reference wherever it's used. Given that the send
being called in useEffect
appears to have the initial value of text
in scope, but the one used in the onClick
of the button
has the newest, my assumptions seem incorrect.
I've also tried adding text
as a dependency of the useEffect
but
How can I keep a current version of my send
function inside of another callback?
Upvotes: 12
Views: 20683
Reputation: 1
Two things to make sure you do when using the useCallback hook:
Upvotes: 0
Reputation: 3244
You are missing send
in your dependencies, take a look at this updated code:
React.useEffect(() => {
const handler = e => {
switch (e.keyCode) {
case 13: // enter
if (e.shiftKey) {
e.preventDefault();
send(); // This will always be called an older version, instead of updated one
}
break;
}
}
document.addEventListener("keydown", handler);
return () => document.removeEventListener("keydown", handler);
}, [send]); // You need to put send here, since it is part of this component, and needs to be updated
The reason It works with it, is because send
function is memoised as well:
const send = React.useCallback(() => {
setText("");
console.log("sending", text);
}, [text]);
so you need to make sure to update to its newer version, with its newer text (which never happened, thats why you got no text in your SHIFT+ENTER)
EDIT: Upon further investigation, it seems to me that the biggest problem was out-of sync text and listener handler.
I have modified the code to work, by removing the listener alltogether, and using onKeyDown
prop directly on textarea. Take a look at this working codepen:
https://codepen.io/antonioerda/pen/zYqYWgx
Upvotes: 11