没有样板的 React 自定义钩子用于关闭
React custom hook without boilerplate for closure
我有一个组件应该监听一些套接字事件。我按如下方式注册它们以避免事件侦听器的虚假 register/unregister 操作(socketListen
returns 侦听器本身的破坏,因此已得到处理)。
useEffect(() => socketListen('name', newName => setName(newName)), []);
现在我有更多此类听众,我想通过编写自定义挂钩来摆脱样板代码
const useListener = (event, callback) => {
useEffect(() => socketListen(event, callback), [event, callback]);
};
然后用作
useListener('name', newName => setName(newName));
打算将重点放在执行的实际操作上,而不是 useEffect 调用上。
现在的问题是我将闭包(调用状态 setter setName
)移交给我的自定义挂钩,这意味着 useListener
中的 callback
在重新渲染时发生了变化,因此再次生成虚假侦听器 registering/unregistering。为了挽救,我可以将闭包包装在 useCallback
中,但这违背了我减少代码混乱的最初目标。
我想我可以看出上述问题的功能性原因(并希望在我的写作中传达这一点),但我想知道是否有一种方便的方法来编写一个辅助函数来完成我想要完成的事情useListener
没有额外的样板代码。我的实际代码使用了更复杂的闭包,但我希望这个示例足以查明我的问题。
您应该能够通过使用参考来摆脱记忆闭包的要求:
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
等于最近传递给 useListener()
的 callback
并且不引用第一次调用 useListener()
.
的陈旧闭包
我有一个组件应该监听一些套接字事件。我按如下方式注册它们以避免事件侦听器的虚假 register/unregister 操作(socketListen
returns 侦听器本身的破坏,因此已得到处理)。
useEffect(() => socketListen('name', newName => setName(newName)), []);
现在我有更多此类听众,我想通过编写自定义挂钩来摆脱样板代码
const useListener = (event, callback) => {
useEffect(() => socketListen(event, callback), [event, callback]);
};
然后用作
useListener('name', newName => setName(newName));
打算将重点放在执行的实际操作上,而不是 useEffect 调用上。
现在的问题是我将闭包(调用状态 setter setName
)移交给我的自定义挂钩,这意味着 useListener
中的 callback
在重新渲染时发生了变化,因此再次生成虚假侦听器 registering/unregistering。为了挽救,我可以将闭包包装在 useCallback
中,但这违背了我减少代码混乱的最初目标。
我想我可以看出上述问题的功能性原因(并希望在我的写作中传达这一点),但我想知道是否有一种方便的方法来编写一个辅助函数来完成我想要完成的事情useListener
没有额外的样板代码。我的实际代码使用了更复杂的闭包,但我希望这个示例足以查明我的问题。
您应该能够通过使用参考来摆脱记忆闭包的要求:
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
等于最近传递给 useListener()
的 callback
并且不引用第一次调用 useListener()
.