【问题标题】:React custom hook without boilerplate for closure没有样板的 React 自定义钩子用于关闭
【发布时间】:2020-10-30 08:32:16
【问题描述】:

我有一个组件应该监听一些套接字事件。我将它们注册如下以避免事件侦听器的虚假注册/注销操作(socketListen 返回侦听器本身的破坏,因此已处理)。

useEffect(() => socketListen('name', newName => setName(newName)), []);

现在我有了更多此类听众,我想通过编写自定义钩子来摆脱样板代码

const useListener = (event, callback) => {
  useEffect(() => socketListen(event, callback), [event, callback]);
};

然后用作

useListener('name', newName => setName(newName));

打算将重点放在执行的实际操作上,而不是 useEffect 调用上。 现在的问题是我将一个闭包(调用状态设置器setName)交给了我的自定义钩子,这意味着useListener 中的callback 在重新渲染时被更改,因此再次生成虚假的侦听器注册/注销。为了挽救,我可以将闭包包裹在 useCallback 中,但这会违背我减少代码混乱的最初目标。

我想我可以看到上述问题的功能原因(并希望在我的写作中传达这一点),但我想知道是否有一种方便的方法来编写一个辅助函数来完成我打算用 @987654329 完成的事情@ 没有额外的样板代码。我的实际代码使用了更复杂的闭包,但我希望这个示例足以确定我的问题。

【问题讨论】:

  • useListener('name', setName)。由useState()useReducer() 返回的setName 已经被记忆,在这种情况下setName 将等同于newName => setName(newName)
  • 我的例子在这方面表现不佳。我应该在prefix is some prop or state 的地方写newName => setName(prefix + newName),即闭包即使有明显的副作用也会改变功能。不过,您在下面的回答确实有很大帮助。

标签: react-hooks closures boilerplate


【解决方案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 很重要,这样在调用socketListen 回调之前不会发生属性访问ref.current。这样ref.current 等于传递给useListener() 的最新callback,并且没有引用从第一次调用useListener() 时的陈旧闭包。

【讨论】:

  • 完美,到目前为止,这似乎满足了我的所有要求。至少样板文件(其中一些是我打算预期的)完全在外部化函数中。这也帮助我进一步理解了钩子(在这个例子中是useRef),因为从这个例子中可以清楚地看到useListener仍然在每次重新渲染时被调用,但是隐藏了useEffect在其他方面的必要更新。参考更新。特别是 const ref = useRef(); ref.current = callback; 不能被 const ref = useRef(callback) 替换,因为这会跳过更新。
猜你喜欢
  • 1970-01-01
  • 2020-08-13
  • 2020-09-22
  • 1970-01-01
  • 2020-09-12
  • 2020-08-27
  • 1970-01-01
  • 2020-12-05
  • 2022-01-10
相关资源
最近更新 更多