我有一个应侦听某些套接字事件的组件。我按如下方式注册它们,以避免事件监听器的虚假注册/取消注册操作(socketListen
返回监听器本身的销毁,因此可以解决)。
useEffect(() => socketListen('name', newName => setName(newName)), []);
现在,由于我有更多此类监听器,我想通过编写自定义钩子来摆脱样板代码
const useListener = (event, callback) => {
useEffect(() => socketListen(event, callback), [event, callback]);
};
然后用作
useListener('name', newName => setName(newName));
意在专注于实际执行的操作,而不是useEffect调用。
现在的问题是,我将闭包(调用状态设置器setName
)移交给了我的自定义钩子,这意味着callback
中的useListener
在重新渲染时已更改,因此再次生成虚假的侦听器注册/取消注册。为了挽救生命,我可以将闭包包装在useCallback
中,但这违背了减少代码混乱的最初目标。
我认为我可以看到上述问题的功能性原因(并希望在我的写作中能够传达出来),但是我想知道是否有方便的方法编写辅助函数来完成我打算用{{ 1}},而无需其他样板代码。我的实际代码使用了更复杂的闭包,但我希望这个示例足以查明我的问题。
答案 0 :(得分:1)
您应该能够通过使用引用来摆脱对关闭记录的要求:
const useListener = (event, callback) => {
const ref = useRef();
ref.current = callback;
useEffect(() => (
socketListen(event, newName => ref.current(newName))
), [event]);
};
重要的是,您编写newName => ref.current(newName)
而不是只写ref.current
,以便在调用ref.current
回调之前不会发生属性访问socketListen
。这样,ref.current
等于传递给callback
的最新useListener()
,并且不会引用从第一次调用useListener()
以来的过时关闭。