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
- I don't want to remove/create/add the listener on every keystroke and
- It's still missing the last character pressed anyway.
How can I keep a current version of my send function inside of another callback?