如何使用使用效果挂钩注册事件?

共6个回答,已解决, 标签: javascript reactjs react-hooks

我正在遵循一个关于如何注册事件与挂钩的 Udemy 课程, 教师给出了以下代码:

  const [userText, setUserText] = useState('');

  const handleUserKeyPress = event => {
    const { key, keyCode } = event;

    if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
      setUserText(`${userText}${key}`);
    }
  };

  useEffect(() => {
    window.addEventListener('keydown', handleUserKeyPress);

    return () => {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  });

  return (

      Feel free to type!
      {userText}

  );

现在效果很好, 但我不相信这是正确的方式。原因是, 如果我理解正确, 在每一次重新渲染, 事件将不断注册和注销每次, 我根本不认为这是正确的方式去做它。

所以我做了一个轻微的修改 useEffect 挂钩下面

useEffect(() => {
  window.addEventListener('keydown', handleUserKeyPress);

  return () => {
    window.removeEventListener('keydown', handleUserKeyPress);
  };
}, []);

通过使用空数组作为第二个参数, 让组件只运行一次效果, 并进行模拟 componentDidMount 。而当我尝试这个结果时, 很奇怪, 在我输入的每一个键上, 它都不是追加的, 而是被覆盖的。

我期待setusertext ( ${userText}${key} );将新的类型化键追加到当前状态并设置为新状态, 但相反, 它忘记了旧状态并使用新状态重写。

这真的是正确的方式, 我们应该注册和取消注册事件的每一个重新渲染?

第1个答案(采用)

处理此类方案的最佳方法是查看您在事件处理程序中正在执行的操作。如果您只是使用以前的状态设置状态, 最好使用回调模式, 并仅在初始装载时注册事件侦听器。如果不使用 callback pattern (https://reactjs.org/docs/hooks-reference.html#usecallback) 侦听器引用及其词法范围正在被事件侦听器使用, 但在新的渲染上创建一个更新的闭包, 从而创建一个新的函数在处理程序中, 您将无法更新状态

const [userText, setUserText] = useState('');

  const handleUserKeyPress = useCallback(event => {
    const { key, keyCode } = event;

    <= 90))="" {="" setusertext(prevusertext=""> `${prevUserText}${key}`);
    }
  }, []);

  useEffect(() => {
    window.addEventListener('keydown', handleUserKeyPress);

    return () => {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  }, [handleUserKeyPress]);

  return (

      Feel free to type!
      {userText}

  );
第2个答案

尝试此操作, 它的工作原理与您的原始代码相同:

useEffect(() => {
  function handlekeydownEvent(event) {
    const { key, keyCode } = event;
    <= 90))="" {="" setusertext(`${usertext}${key}`);="" }="" }="" document.addeventlistener('keyup',="" handlekeydownevent)="" return="" ()=""> {
    document.removeEventListener('keyup', handlekeydownEvent)
  }
}, [userText])

因为在您 useEffect() 的方法中, 它依赖于 userText 变量, 但您不将其放在第二个参数 userText 中, 否则它将始终绑定到 '' 具有参数的初始值 []

你不需要这样做, 只是想让你知道为什么你的第二个解决方案不工作。

第3个答案

对于用例, useEffect 需要一个依赖项数组来跟踪更改, 并根据依赖项确定是否重新呈现。始终建议将依赖项数组传递给 useEffect 。请参见以下代码:

我介绍了 useCallback 钩子。

const { useCallback, useState, useEffect } = React;

  const [userText, setUserText] = useState("");

  const handleUserKeyPress = useCallback(event => {
    const { key, keyCode } = event;

    <= 90))="" {="" setusertext(prevusertext=""> `${prevUserText}${key}`);
    }
  }, []);

  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);

    return () => {
      window.removeEventListener("keydown", handleUserKeyPress);
    };
  }, [handleUserKeyPress]);

  return (

      {userText}

  );

Edit q98jov5kvq

第4个答案

您需要一种方法来跟踪以前的状态。useState仅帮助您跟踪当前状态。从文档中, 有一种方法可以通过使用另一个钩子来访问旧状态。

const prevRef = useRef();
useEffect(() => {
  prevRef.current = userText;
});

我已经更新了你的例子来使用它。而且很管用。

const { useState, useEffect, useRef } = React;

const App = () => {
  const [userText, setUserText] = useState("");
  const prevRef = useRef();
  useEffect(() => {
    prevRef.current = userText;
  });
  const handleUserKeyPress = event => {
    const { key, keyCode } = event;
if (keyCode === 32 || (keyCode >= 65 && keyCode <= 90)) {
  setUserText(`${prevRef.current}${key}`);
}  };
  useEffect(() => {
    window.addEventListener("keydown", handleUserKeyPress);
return () => {
  window.removeEventListener("keydown", handleUserKeyPress);
};  }, []);
  return (

      Feel free to type!
      {userText}

  );
};
第5个答案

在第二种方法中, useEffect 绑定仅一次, 因此 userText 永远不会更新。一种方法是维护一个局部变量, 该变量将随 userText 每个按键上的对象一起更新。

  const [userText, setUserText] = useState('');
  let local_text = userText
  const handleUserKeyPress = event => {
    const { key, keyCode } = event;

    <= 90))="" {="" local_text="`${userText}${key}`;" setusertext(local_text);="" }="" };="" useeffect(()=""> {
    window.addEventListener('keydown', handleUserKeyPress);

    return () => {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  }, []);

  return (

      Feel free to type!
      {userText}

  );

就我个人而言, 我不喜欢这个解决方案, anti-react 感觉和我认为第一种方法足够好, 设计成这样使用。

第6个答案

您没有访问已更改的使用文本状态的权限。你可以把它压缩到普林邦。将状态存储在变量中, 例如: 状态如下所示:

const App = () => {
  const [userText, setUserText] = useState('');

  useEffect(() => {
    let state = ''

    const handleUserKeyPress = event => {
      const { key, keyCode } = event;
      <= 90))="" {="" state="" +="`${key}`" setusertext(state);="" }="" };="" window.addeventlistener('keydown',="" handleuserkeypress);="" return="" ()=""> {
      window.removeEventListener('keydown', handleUserKeyPress);
    };
  }, []);

  return (

      Feel free to type!
      {userText}

  );
};

相关问题

如何从异步调用返回响应? 循环中的 JavaScript 闭包--简单的实际示例 如何从异步调用返回响应? 如何使用使用效果挂钩注册事件? 未知的浏览器 kaios 在 Set间隙中使用 "反应状态" 挂钩时状态不更新 根据恒定条件调用反应钩是否安全?