如何更新 useEffect 中的卸载处理程序而不重复触发它?

tem*_*ame 5 reactjs react-hooks

今天,另一位开发人员向我提出了一个棘手的问题。她提出以下建议:

A.) 她想在模式关闭时触发一个事件。
B.) 她不希望该事件在任何其他时间触发。
C.) 事件必须与组件的状态保持同步。

一个基本的例子是这样的:

const ModalComponent = () => {
    const [ eventData, setEventData ] = useState({ /* default data */ });

    useEffect(() => {
        return () => {
            // happens anytime dependency array changes
            eventFire(eventdata);
        };
    }, [eventData]);
   
    return (
       <>
         // component details omitted, setEventData used in here
       </>
    );
};
Run Code Online (Sandbox Code Playgroud)

目的是当模式关闭时,事件触发。但是,用户与模态的交互会导致状态更新,从而更改 的值eventData。这就引出了问题的核心:

  1. 忽略依赖项eventData数组useEffect会导致它仅在模式关闭时触发,但该值已过时。这是安装组件时的值。
  2. 每当数据更改以及组件重新渲染和更新时,放置eventDatauseEffect依赖项数组中都会导致事件反复触发useEffect

解决这个问题的办法是什么?如何访问最新数据但仅在卸载时对其进行操作?

Die*_*lgo 5

存储eventData在参考中。

const [eventData, setEventData] = useState({ /* default data */ });
const ref = useRef();

ref.current = eventData;

useEffect(() => () => eventFire(ref.current), []);
Run Code Online (Sandbox Code Playgroud)

这将使该值始终保持最新,因为它不会被锁定到函数闭包中,并且无需每次eventData更改时触发效果。

此外,您可以将此逻辑提取到自定义挂钩中。

function useStateMonitor(state) {
  const ref = useRef();
  ref.current = state;
  return () => ref.current;
}
Run Code Online (Sandbox Code Playgroud)

用法会是这样的

const [eventData, setEventData] = useState({ /* default data */ });
const eventDataMonitor = useStateMonitor(eventData);

useEffect(() => () => eventFire(eventDataMonitor()), []);
Run Code Online (Sandbox Code Playgroud)

这是一个工作示例