I want React to render key presses from a non-React context, more specifically the string array keys
:
import * as React from "react";
import { render } from "react-dom";
let keys: string[] = [];
function handleKeypress(event: any) {
keys.push(event.key);
console.log(keys);
// there will be more code here unrelated to React.
}
document.removeEventListener("keypress", handleKeypress);
document.addEventListener("keypress", handleKeypress);
function App() {
const [keysState, setKeysState] = React.useState<string[]>([]);
React.useEffect(() => {
function updateKeysState() {
setKeysState([...keys]);
}
// if you uncomment this, the code inside useEffect will run forever
// updateKeysState()
console.log("Hello world");
}, [keysState]);
return (
<div>
{keys.map((key: string, id) => (
<li key={id}>{key}</li>
))}
</div>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
I almost accomplished that ... the problem is, the code inside React.useEffect
runs in an infinite loop.
I thought passing [keysState]
as a second argument to React.useEffect
would stop the infinite loop. But it didn't.
Why is this and how to fix it?
Live code: https://codesandbox.io/s/changing-props-on-react-root-component-forked-eu16oj?file=/src/index.tsx
CodePudding user response:
The best way would be to integrate the non-React code into the App, so that setting state as a result of a keypress is natural and trivial.
function App() {
const [keys, setKeys] = React.useState<string[]>([]);
useEffect(() => {
function handleKeypress(event: KeyboardEvent) {
setKeys([...keys, event.key]);
// There will be more code here that's unrelated to React.
}
document.addEventListener("keypress", handleKeypress);
return () => {
document.removeEventListener("keypress", handleKeypress);
};
}, []);
Then you can drop your current React.useEffect
(and its infinite loop) entirely.
If that's not an option, you'd have to trigger the React state setter from outside of React - any way you look at it, that'll be pretty ugly. I suppose you could assign it to an outside variable:
let setKeysOuter;
function handleKeypress(event: KeyboardEvent) {
setKeysOuter?.(keys => [...keys, event.key]);
// There will be more code here that's unrelated to React.
}
function App() {
const [keys, setKeys] = React.useState<string[]>([]);
setKeysOuter = setKeys;
CodePudding user response:
You need to pass two arguments to useEffect:
1 => A setup function with setup code that connects to that system. It should return a cleanup function with cleanup code that disconnects from that system. 2 => A list of dependencies including every value from your component used inside of those functions.