React:窗口事件订阅

发布于 2025-01-11 09:31:18 字数 1704 浏览 0 评论 0原文

我正在研究 React 并尝试创建某种键盘训练应用程序。

我想向窗口对象添加一个 keypress 事件监听器来获取字母,然后更新我使用 useState() 挂钩创建的状态。

因此,我在 useEffect 挂钩中添加了事件侦听器。与自定义处理程序。但在这种情况下,setPhrase 函数似乎效果不佳。

预期结果: 在每个正确的按键后,按 phrase.writing 将此按键添加为文本,phrase.left 将此字母作为子字符串;

实际结果: 状态每次都会更新,因此在 setPhrase 调用后状态不会更新

代码:

export default () => {
  const [initialPhrase] = useState("Test Phrase");
  const [phrase, setPhrase] = useState({
    left: initialPhrase,
    written: "",
  });

  const handleKeyPress = (event) => {
    const requiredLetter = phrase.left.charAt(0);

    if (requiredLetter === event.key) {
      setPhrase({
        written: phrase.written + event.key,
        left: phrase.left.substring(1, phrase.left.length),
      });
    }
    console.debug(phrase, requiredLetter, event.key, phrase);
  };

  useEffect(() => {
    window.addEventListener("keypress", handleKeyPress);
    return () => window.removeEventListener("keypress", handleKeyPress);
  }, []);

  return (
    <div>
      <p className="phrase">
        <span className="phrase--part phrase--part__written">
          {phrase.written}
        </span>
        <span className="phrase--part phrase--part__left">{phrase.left}</span>
      </p>
    </div>
  );
};

Playground: https://stackblitz.com/edit/react-qgbjaf?file= src/index.js

注意:如果将此处理程序放置到组件内的任何元素,它将按预期工作

I am getting into React and trying to create some kind of keyboard trainer app.

I want to add a keypress event listener to the window object to get the letters, and then update the state I created with useState() hook.

So I add the event listener inside useEffect hook. with the custom handler. But the setPhrase function doesn't seem to work well in this case.

Expected result:
After each correct key press the phrase.written to add this key as text, phrase.left to substring by this letter;

Actual result:
State is renewed every time, so the state doesn't update after setPhrase call

The code:

export default () => {
  const [initialPhrase] = useState("Test Phrase");
  const [phrase, setPhrase] = useState({
    left: initialPhrase,
    written: "",
  });

  const handleKeyPress = (event) => {
    const requiredLetter = phrase.left.charAt(0);

    if (requiredLetter === event.key) {
      setPhrase({
        written: phrase.written + event.key,
        left: phrase.left.substring(1, phrase.left.length),
      });
    }
    console.debug(phrase, requiredLetter, event.key, phrase);
  };

  useEffect(() => {
    window.addEventListener("keypress", handleKeyPress);
    return () => window.removeEventListener("keypress", handleKeyPress);
  }, []);

  return (
    <div>
      <p className="phrase">
        <span className="phrase--part phrase--part__written">
          {phrase.written}
        </span>
        <span className="phrase--part phrase--part__left">{phrase.left}</span>
      </p>
    </div>
  );
};

Playground:
https://stackblitz.com/edit/react-qgbjaf?file=src/index.js

Note: if you place this handler to any element inside component, it is going to work just as expected

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

柠北森屋 2025-01-18 09:31:18

问题是在短语状态更新后,handleKeyPress 方法没有更新。因此,您需要将handleKeyPress方法添加到useEffect依赖中。现在,在每个短语状态更新之后,该方法都会获取短语 Stackblitz fork 的正确状态

https://stackblitz.com/edit/react-bx5qp7?file=src/index.js

import React, { useState, useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import './style.css';

const KeyboardTrainer = () => {
  const [initialPhrase] = useState('Test Phrase');
  const [phrase, setPhrase] = useState({
    left: initialPhrase,
    written: '',
  });

  const handleKeyPress = (event) => {
      const requiredLetter = phrase.left.charAt(0);
      if (requiredLetter === event.key) {
        setPhrase({
          written: phrase.written + event.key,
          left: phrase.left.substring(1, phrase.left.length),
        });
      }
    }

  useEffect(() => {
    window.addEventListener('keypress', (e) => {
      handleKeyPress(e);
    });
    return () => window.removeEventListener("keypress", handleKeyPress);
  }, [handleKeyPress]);

  return (
    <div>
      <p className="phrase">
        <span className="phrase--part phrase--part__written">
          {phrase.written}
        </span>
        <span className="phrase--part phrase--part__left">{phrase.left} 
        </span> 
      </p>
    </div>
     )};

ReactDOM.render(<KeyboardTrainer />, document.getElementById('root'));

The problem is that the handleKeyPress Method is not updated after the phrase state updated. Therefore, you need to add the handleKeyPress method to the useEffect dependency. Now after each phrase state update, the method gets the correct state of phrases

Stackblitz fork: https://stackblitz.com/edit/react-bx5qp7?file=src/index.js

import React, { useState, useEffect, useCallback } from 'react';
import ReactDOM from 'react-dom';
import './style.css';

const KeyboardTrainer = () => {
  const [initialPhrase] = useState('Test Phrase');
  const [phrase, setPhrase] = useState({
    left: initialPhrase,
    written: '',
  });

  const handleKeyPress = (event) => {
      const requiredLetter = phrase.left.charAt(0);
      if (requiredLetter === event.key) {
        setPhrase({
          written: phrase.written + event.key,
          left: phrase.left.substring(1, phrase.left.length),
        });
      }
    }

  useEffect(() => {
    window.addEventListener('keypress', (e) => {
      handleKeyPress(e);
    });
    return () => window.removeEventListener("keypress", handleKeyPress);
  }, [handleKeyPress]);

  return (
    <div>
      <p className="phrase">
        <span className="phrase--part phrase--part__written">
          {phrase.written}
        </span>
        <span className="phrase--part phrase--part__left">{phrase.left} 
        </span> 
      </p>
    </div>
     )};

ReactDOM.render(<KeyboardTrainer />, document.getElementById('root'));
向日葵 2025-01-18 09:31:18

在事件处理程序方法中,您无法达到更新的状态,因为状态变量不会在 EventListener 内更新自身。只需像这样使用 useRef :

const phraseRef = useRef({
    left: initialPhrase,
    written: '',
  })

  const handleKeyPress = (event) => {
    const requiredLetter = phraseRef.current.left.charAt(0);

    if (requiredLetter === event.key) {
      setPhrase({
        written: phraseRef.current.written + event.key,
        left: phraseRef.current.left.substring(1, phraseRef.current.left.length),
      });
    
      phraseRef.current = {
        written: phraseRef.current.written + event.key,
        left: phraseRef.current.left.substring(1, phrase.left.length),
      }
    }
    console.debug(phrase, requiredLetter, event.key, phrase);
  };

in your event handler method you cant reach the updated state, because state variables don't update itself inside an EventListener. just use useRef like this:

const phraseRef = useRef({
    left: initialPhrase,
    written: '',
  })

  const handleKeyPress = (event) => {
    const requiredLetter = phraseRef.current.left.charAt(0);

    if (requiredLetter === event.key) {
      setPhrase({
        written: phraseRef.current.written + event.key,
        left: phraseRef.current.left.substring(1, phraseRef.current.left.length),
      });
    
      phraseRef.current = {
        written: phraseRef.current.written + event.key,
        left: phraseRef.current.left.substring(1, phrase.left.length),
      }
    }
    console.debug(phrase, requiredLetter, event.key, phrase);
  };
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文