简体   繁体   English

如何停止这个 React useEffect 中的无限循环?

[英]How to stop the infinite loop inside this React useEffect?

I want React to render key presses from a non-React context, more specifically the string array keys :我希望 React 从非 React 上下文呈现按键,更具体地说是字符串数组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.我几乎完成了……问题是, React.useEffect中的代码在无限循环中运行。

I thought passing [keysState] as a second argument to React.useEffect would stop the infinite loop.我认为将[keysState]作为第二个参数传递给React.useEffect会停止无限循环。 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实时代码: https://codesandbox.io/s/changing-props-on-react-root-component-forked-eu16oj?file=/src/index.tsx

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.最好的方法是将非 React 代码集成到应用程序中,这样设置 state 作为按键的结果是自然而微不足道的。

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.然后你可以完全放弃你当前的React.useEffect (和它的无限循环)。

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.如果这不是一个选项,您将不得不从 React 外部触发 React state setter - 无论您怎么看,这都会非常难看。 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;

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM