首页 > 解决方案 > 如何在 useEffect IAW 'exhaustive-deps' lint 规则中读取和修改状态

问题描述

我有一张有叠加层的地图。我有一个效果,当基础数据发生变化时,删除旧的叠加层并重新绘制一个新的叠加层。

值得注意的是,我正在使用,从我读到"react-hooks/exhaustive-deps"的所有内容看来,仅仅删除依赖项并不是正确的答案(但它确实有效)。overlay

值得注意的是,数据是传入的道具。

useEffect(() => {
    // remove the overlay if it is there,
    // I should have overlay as a dependency per exhaustive-reps react eslint rule
    if (overlay) map.removeOverlays(overlay);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // Store the new overlay so I can remove it later if the data changes
    // Doesn't need to be done right away though, just before next render
    setOverlay(newOverlay);

    // Show the new overlay on my map
    map.addOverlays(newOverlay);
  }, [map, overlay, data, generateNewOverlay]);

这当然将是一个无限循环,因为我正在设置覆盖并使其成为依赖项。我也不喜欢在效果中立即使用 setState,因为它会导致另一个渲染。我错过了什么?我如何实现这个逻辑?

我确实阅读了大约 5 个类似标题的问题,但他们没有回答我的问题。

类似的问题,但在保持详尽的代表 deps lint 规则时不问,这对他们来说不是一个因素,因为他们在更改状态之前没有读取状态。


编辑:

我仍然有 useState 和 reducer 应该是纯的问题。我遇到 ( overlay) 问题的依赖项的目的是我必须检查覆盖是否存在。如果是这样,我需要做一件非纯粹的事情,即在设置我的新覆盖之前从地图中删除该覆盖。这行代码使它不纯:

if (overlay) map.removeOverlays(overlay); 

如果没有那条线,我永远不需要overlay在我的依赖项列表中,这整个事情也不会成为一个因素。正如您所看到的,由于那条线,接受的答案并不纯粹。

useReducer 答案在减速器之外有这条线,所以overlay应该是一个依赖项,或者它需要进入减速器。第一个选项是我试图避免的"react-hooks/exhaustive-deps",第二个答案并不纯粹。

感谢任何帮助。


最终编辑:

我发现解决这个问题的最佳方法是正确使用 useEffect 中的清理。我需要状态,以便以后可以从地图中删除内容。

useEffect(() => {
    // remove the overlay if it is there,
    // I originally needed this to store the overlay so I could remove it later, but if I use cleanup properly this isn't needed
    // if (overlay) map.removeOverlays(overlay);    

    // Generate the new overlay based on the new data (useCallback function)
    const newOverlay = generateNewOverlay(data)

    // Store the new overlay so I can remove it later if the data changes
    // Doesn't need to be done right away though, just before next render (This is what a cleanup is!)
    // setOverlay(newOverlay);

    // Show the new overlay on my map
    map.addOverlays(newOverlay);

    // New code below
    return () => {map.removeOverlays(newOverlay)};
  }, [map, data, generateNewOverlay]);

标签: reactjsreact-hooks

解决方案


我认为这里的问题是您适应此 linter 规则的方法过于被动。您在useEffect任何时候运行回调中的所有代码,但您真的只想在data更改时执行这些代码。

因此,您可以在代码中反映这一点:

const [shouldUpdateOverlay, setShouldUpdateOverlay] = useState(false);

useEffect(() => {
    if (shouldUpdateOverlay) {
        // remove the overlay if it is there,
        // I should have overlay as a dependency per exhaustive-reps react eslint rule
        if (overlay) map.removeOverlays(overlay);    

        // Generate the new overlay based on the new data (useCallback function)
        const newOverlay = generateNewOverlay(data);

        // Show the new overlay on my map
        map.addOverlays(newOverlay);

        // Store the new overlay so I can remove it later if the data changes
        // Doesn't need to be done right away though, just before next render
        setOverlay(newOverlay);
        setShouldUpdateOverlay(false);
    }
}, [map, overlay, shouldUpdateOverlay data, generateNewOverlay]);

// use from event handlers, etc.
const updateData = (newData) => {
    setData(newData);
    setShouldUpdateOverlay(true);
};

实际上,这是一种过度劳累的方法来完成您可以通过将[data]作为唯一依赖项来实现的相同的事情useEffect,因为这确实是唯一重要的依赖项。但是,我相信以上内容应该可以让您在不违反 linter 规则的情况下实现您想要做的事情。


推荐阅读