使用React挂钩时事件监听器功能发生变化

时间:2019-09-30 07:40:17

标签: javascript reactjs addeventlistener react-hooks usecallback

我有一个组件,它通过addEventListenerremoveEventListener在多个地方使用事件监听器。使用onMouseMove之类的组件方法是不够的,因为我还需要检测组件外部的事件。

我在组件中使用钩子,其中有一些在末尾具有依赖项数组,尤其是useCallback(eventFunction, dependencies)带有要与侦听器一起使用的事件函数。依赖关系通常是使用useState声明的有状态变量。

据我所知,该功能的身份在add/remove EventListener中是很重要的,因此,如果在两者之间进行功能更改,它将无法正常工作。最初,我尝试管理钩子,以使事件函数不会更改addremove之间的身份,但是由于函数对状态的依赖性很快,该函数变得难以处理。

因此,最后我想到了以下模式:由于setter函数(useState的第二个输入参数)将当前状态作为参数,因此我可以拥有事件函数,该函数在第一个之后永远不会改变渲染(我们仍然称其为mount吗?),但仍可以访问最新的有状态变量。一个例子:

import React, { useCallback, useEffect, useState } from 'react';

const Component = () => {
  const [state, setState] = useState(null);

  const handleMouseMove = useCallback(() => {
    setState((currentState) => {
      // ... do something that involves currentState ...
      return currentState;
    });
  }, []);

  useEffect(() => {
    window.addEventListener('mousemove', handleMouseMove);
    return () => {
      window.removeEventListener('mousemove', handleMouseMove);
    };
  }, [/* ... some parameters here ... */]);

  // ... more effects etc ...

  return <span>test</span>;
};

(这是一个简化的插图)。

这似乎很好用,但我不确定是否感觉很正确-使用永远不会改变状态的setter函数,就像黑客访问当前状态一样。

此外,对于需要几个状态变量的事件函数,我必须嵌套setter调用。

还有另一种模式可以更好地处理这种情况吗?

2 个答案:

答案 0 :(得分:3)

  

据我所知,该函数的身份在添加/删除EventListener中很重要,因此,如果函数之间的更改不起作用,则不起作用。

虽然这是事实,但我们不必为设置甚至根本不更改身份的功能而走极端。

简单步骤如下: 使用useCallback声明事件函数-useCallback的依赖项列表应包括函数依赖的所有有状态变量。

使用useEffect添加事件监听器。返回清除函数,该函数将删除事件侦听器。 useEffect的依赖项列表应包括事件侦听器函数本身,以及效果函数可能正在使用的任何其他有状态变量。

这样,当事件侦听器使用的任何有状态变量发生更改时,甚至侦听器的标识也会发生更改,这将触发效果的运行,但是在运行之前,运行效果的前一次返回的效果清理函数将被运行,并正确删除旧的事件侦听器,然后再添加新的事件侦听器。

以下方面的内容:

const Component = () => {
    const [state, setState] = useState();

    const eventListner = useCallback(() => {
        console.log(state); // use the stateful variable in event listener
    }, [state]);

    useEffect(() => {
        el.addEventListner('someEvent', eventListner);
        return () => el.removeEventListener('someEvent', eventListner);
    }, [eventListener]);
}

答案 1 :(得分:1)

@ckedar的解决方案可以解决此问题,但是它存在性能问题,当eventListener更改时,react将删除dom上的addEvent。

您可以使用useRef()代替useState(),如果要更改监听状态,可以使用useStateRef()

import React, { useEffect, useRef, useState } from 'react';

export default function useStateRef(initialValue:any): Array<any>{
  const [value, setValue] = useState(initialValue);
  const ref = useRef(value);
  useEffect(() => {
    ref.current = value;
  },[value])
  return [value,setValue,ref];
}