React 不允许我以完全合理的方式使用 useEffect

React won't let me use `useEffect` in a completely reasonable way

我创建了以下辅助函数,因为 React 中的功能组件没有挂载和卸载事件。我不在乎别人怎么说; useEffect 不是等价物。它可以像我下面演示的那样:

//eslint-disable-next-line
export const useMount = callback => useEffect(callback, []);

//eslint-disable-next-line
export const useUnmount = callback => useEffect(() => callback, []);

React 不允许我这样做,因为我在技术上从非组件函数调用 useEffect。我这样做是因为当我将 useEffect 用作​​挂载或卸载事件时,它会用关于不在依赖项列表中包含某些内容的无意义警告污染我的终端。我知道,我应该这样做...

export default function MusicPlayback(...) {
    ...
    useEffect(() => stopMusic, []);
    ...
}

但随后我收到一条警告,称 stopMusic 未作为依赖项包含在内。但我不希望它成为依赖项,因为那样 useEffect 将不再是卸载事件并且 stopMusic 将在每次渲染时调用。

我知道是 eslint 在警告我,我可以使用 //eslint-disable-next-line,但是在每个需要卸载处理程序的文件中都很难看。

据我所知,如果不在任何地方绝对使用 //eslint-disable-next-line,就无法拥有卸载处理程序。有解决办法吗?

好的,依赖性检查是有原因的,即使你认为它不应该存在。

  useEffect(() => {
    stopMusic()
    ...
  }, [stopMusic, ...])

说一下stopMusic,假设这是另一个第三方的全局函数。如果实例永远不会改变,那么您应该将其作为依赖项触发,因为它不会造成伤害。

如果 stopMusic 实例确实改变了,那么你需要问问自己为什么不想把它作为一个依赖项,因为它可能会不小心调用旧的 stopMusic

现在,假设您对所有这些都很好,但仍然不希望它与 stopMusic 连接,那么请考虑使用 ref.

  const ref = useRef({ stopMusic })

  useEffect(() => ref.current.stopMusic(), [ref])

无论您如何理解,它都必须取决于某些东西,也许您的业务逻辑不想这样做。但从技术上讲,只要您需要调用不属于 useEffect 的内容,它就需要是一个依赖项。否则从 useEffect 的角度来看,这是一个不同步的问题。 ref(或任何对象)的要点是故意进入这种不同步状态。

当然,如果你真的讨厌这个linter规则,我相信你可以禁用它。

注意

React 社区正在提议一种在未来为您添加这些依赖项的方法。其背后的原因是 React 旨在对单程列车中的数据作出反应。

这就是我在卸载时停止播放音乐的最终结果。

export default function MusicPlayback(...) {
    const [playMusic, stopMusic] = useMagicalSoundHookThingy(myMusic);
    const stopMusicRef = useRef(stopMusic);
    stopMusicRef.current = stopMusic; // Gotta do this because stopMusic no longer works after render.
    ...
    useEffect(() => {
        const stopMusicInHere = stopMusicRef.current; // Doing this to avoid a warning telling me that when called the ref will probably no longer be around.
        return stopMusicInHere;
    }, [stopMusicRef]);
    ...
}

像这样使用 ref 没有意义。这只是一个愚弄 eslint 的聪明 hack。我们正在将每次渲染都会改变的东西打包成不会改变的东西。这就是我们所做的一切。

我遇到的问题是与我交互的实体是静态的。但是与该实体(即函数 stopMusic)通信的方式是短暂的。因此,useEffect 确定依赖关系的蛮力方法不够细致,不足以真正表明某些依赖关系是否真的发生了变化。只是调用该依赖项的 tiddlywinks,由钩子创建的函数和对象。也许这就是钩子作者的错。也许 tiddlywinks 应该保持与实体相同的生命周期。

我非常喜欢 React,但这是我有一段时间的烦恼,我厌倦了人们告诉我我应该只包含所有依赖项 eslint 要求,就好像我真的没有了解实际涉及的依赖关系。在 React 程序中完全没有任何副作用可能是理想的,并且依赖像 Redux 这样的数据存储管道来提供任何上下文。但这是真实的世界,总会有生命周期不连贯的实体。在后台播放的音乐就是这样一种实体。生活就是这样。