反应自定义钩子,没有用于封闭的样板

时间:2020-10-30 08:32:16

标签: react-hooks closures boilerplate

我有一个应侦听某些套接字事件的组件。我按如下方式注册它们,以避免事件监听器的虚假注册/取消注册操作(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}},而无需其他样板代码。我的实际代码使用了更复杂的闭包,但我希望这个示例足以查明我的问题。

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()以来的过时关闭。