首页 > 解决方案 > React Hooks + window.addEventListener in useEffect

问题描述

我正在尝试console.log用户正在输入的 html 元素。

为此,我实现了以下钩子:

export const useGetActiveElement = () => {

const [activeElement, setActiveElement] = useState()

useEffect(() => {
  if (window){
    window.addEventListener('keyup', setCorrectElement);
  }
  return () => {
    window.removeEventListener('keyup', setCorrectElement);
  }
}, [])

const setCorrectElement = (element) => {
  if(element.target.tagName === 'INPUT' || element.target.tagName === 'TEXTAREA') {
    setActiveElement(element.target)
  }
}

return activeElement}
export default () => {
        const activeElement = useGetActiveElement()

        console.log(activeElement)  <==== Here! Only prints the value 2 or 3 times... 

        return <AnotherComponent />
}

出于某种我不明白的原因,该值在控制台中打印了几次,但在某些时候,它就不再打印了。

我不确定这是否是参考问题......但找不到合理的解释。

想法?

谢谢

标签: reactjsreact-hooksuse-effect

解决方案


在您的初始渲染中,您的钩子的内部状态初始化为undefined.

当您按下一个键时,您输入您的回调并尝试将状态设置为 HTML 元素。React 做了几次优化尝试,并将旧状态与新状态进行比较。如果值不同,它将安排重新渲染使用此钩子的任何组件。由于undefined和 HTML 元素不同,React 将导致您的组件重新渲染,并且您的 console.log 消息将显示当前元素。


当您第二次按下某个键时,您将尝试再次将状态设置为 HTML 元素。然而这一次,这个值作为之前的状态,这意味着之前使用这个状态的组件不需要重新渲染。

然而,React 的并发有一个已知的怪癖,React 实际上会再渲染一次以确定当前提交的版本。

https://github.com/facebook/react/issues/17474#issuecomment-560942800

我们不知道当前提交了两个版本中的哪一个。当这种歧义发生时,我们必须过度渲染一次,然后我们知道两个版本是相同的并且没关系。

因此,您的组件将一次又一次地呈现第二次,您的 console.log 消息将显示当前元素。


然而,当你第三次按下某个键时,React 会再次注意到它是同一个引用,但这次不会重新渲染你的组件。

因此,您的组件不会第三次重新渲染,并且您的控制台日志也不会显示。


推荐阅读