如何修复React Hook useEffect中缺少的依赖项

时间:2019-04-25 00:35:14

标签: reactjs eslint create-react-app react-hooks

在使用React 16.8.6(以前的版本在16.8.3上很好)时,我尝试阻止获取请求中的无限循环时出现此错误

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

我一直找不到能够很好地阻止无限循环的解决方案。我想远离使用useReducer()。我确实在enter image description here的讨论中找到了You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.的可能解决方案,但我对正在做的事情没有把握,所以我还没有尝试实现它。

我具有当前的设置https://github.com/facebook/react/issues/14920,唯一的评论是关于useCallback()的我不熟悉。

我目前如何使用useEffect()(与componentDidMount()类似,我一开始只想运行一次)

useEffect(() => {
    fetchBusinesses();
  }, []);
const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

17 个答案:

答案 0 :(得分:150)

您可以将其直接设置为useEffect回调:

useEffect(fetchBusinesses, [])

它将仅触发一次,因此请确保正确设置了所有函数的依赖项(与使用componentDidMount/componentWillMount...相同)


编辑02/21/2020

只是为了完整性:

1。将函数用作useEffect回调(如上所述)

useEffect(fetchBusinesses, [])

2。在useEffect()内声明函数

useEffect(() => {
  function fetchBusinesses() {
    ...
  }
  fetchBusinesses()
}, [])

3。用useCallback()

进行记忆

在这种情况下,如果函数中具有依赖项,则必须将它们包含在useCallback依赖项数组中,如果函数的参数发生更改,这将再次触发useEffect。除此之外,还有很多样板...因此只需像useEffect一样直接将函数传递给1. useEffect(fetchBusinesses, [])

const fetchBusinesses = useCallback(() => {
  ...
}, [])
useEffect(() => {
  fetchBusinesses()
}, [fetchBusinesses])

4。禁用eslint的警告

useEffect(() => {
  fetchBusinesses()
}, []) // eslint-disable-line react-hooks/exhaustive-deps

答案 1 :(得分:5)

如果除了效果之外没有在其他地方使用fetchBusinesses方法,则可以将其移至效果中并避免出现警告

useEffect(() => {
    const fetchBusinesses = () => {
       return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
  fetchBusinesses();
}, []);

但是,如果您在渲染之外使用fetchBusinesses,则必须注意两点

  1. 如果您不通过fetchBusinesses作为方法,并且在挂载过程中使用它的封闭方式,将不会有任何问题。
  2. 您的方法是否取决于它从封闭的闭包中接收到的某些变量,对您而言不是这种情况。
  3. 在每个渲染上,都会重新创建fetchBusinesses,因此将其传递给useEffect会引起问题。因此,如果要将fetchBusinesses传递给依赖项数组,则必须记住它。

总结起来,我想说的是,如果您在fetchBusinesses之外使用useEffect,则可以使用// eslint-disable-next-line react-hooks/exhaustive-deps禁用规则,否则可以将方法移到useEffect内

答案 2 :(得分:3)

只需将函数作为参数传递给useEffect数组...

useEffect(() => {
   functionName()
}, [functionName])

答案 3 :(得分:3)

const [mount, setMount] = useState(false)
const fetchBusinesses = () => { 
   //function defination
}
useEffect(() => {
   if(!mount) {
      setMount(true);
      fetchBusinesses();
   }
},[fetchBusinesses]);

这是非常简单的解决方案,您无需覆盖es-lint警告。只需维护一个标志来检查组件是否已安装。

答案 4 :(得分:2)

./src/components/BusinessesList.js
Line 51:  React Hook useEffect has a missing dependency: 'fetchBusinesses'.
Either include it or remove the dependency array  react-hooks/exhaustive-deps

不是JS / React错误,而是警告警告。

这是在告诉您钩子取决于函数fetchBusinesses,因此您应将其作为依赖项传递。

useEffect(() => {
  fetchBusinesses();
}, [fetchBusinesses]);

如果fetchBusinessess不在运行中,则不会真正改变任何东西,但是警告会消失。

更多:https://github.com/facebook/react/issues/14920

答案 5 :(得分:2)

您尝试这种方式

const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };

useEffect(() => {
    fetchBusinesses();
  });

为您服务。 但是我的建议是尝试这种方式也对您有用。 比以前更好。我使用这种方式:

useEffect(() => {
        const fetchBusinesses = () => {
    return fetch("theURL", {method: "GET"}
    )
      .then(res => normalizeResponseErrors(res))
      .then(res => {
        return res.json();
      })
      .then(rcvdBusinesses => {
        // some stuff
      })
      .catch(err => {
        // some error handling
      });
  };
        fetchBusinesses();
      }, []);

如果您基于特定ID获得数据,则添加回调useEffect [id],然后无法向您显示警告 React Hook useEffect has a missing dependency: 'any thing'. Either include it or remove the dependency array

答案 6 :(得分:1)

就我而言,它对我的​​局部变量 organization 有此警告,当我将 organization 放入依赖数组 useEffect 时,它会无限获取。因此,如果您有一些像我这样的问题,请使用 useEffect 与依赖数组和拆分:
因为如果你有多个修改状态的api调用,它会多次调用useEffect
来自:

  const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
    dispatch(getMembers({}))
  }, [dispatch, organization])

致:

  const { organization } = useSelector(withOrganization)
  const dispatch = useDispatch()

  useEffect(() => {
    dispatch(getOrganization({}))
    dispatch(getSettings({}))
  }, [dispatch, organization])

  useEffect(() => {
    dispatch(getMembers({}))
  }, [dispatch])

答案 7 :(得分:1)

如果您在 useEffect 内部使用的变量在组件内部定义或作为 prop 传递给组件,则会发生此警告。由于您在同一组件内定义了 fetchBusinesses(),因此您必须将其传递给依赖项数组。

但是如果您导入 fetchBusinesses() 然后在 useEffect 中使用它,则不需要将其添加到依赖项数组中。这就是我们实际设置 redux 应用程序的方式:我们总是导入我们的 action creators 并在 useEffect 中运行它,而不将它添加到依赖项数组中。

useMemo 也是如此。

答案 8 :(得分:1)

我只想像[fetchBusinesses

一样先开始运行[componentDidMount()

您可以将fetchBusinesses完全拉出组件:

const fetchBusinesses = () => { // or pass some additional input from component as args
  return fetch("theURL", { method: "GET" }).then(n => process(n));
};

const Comp = () => {
  React.useEffect(() => {
    fetchBusinesses().then(someVal => {
      // ... do something with someVal
    });
  }, []); // eslint warning solved!
  return <div>{state}</div>;
};

这不仅可以提供简单的解决方案,而且可以解决详尽的下降警告。 fetchBusiness现在可以更好地测试,并且可以简化Comp,因为它位于React树之外的模块范围内。

在外部将fetchBusinesses移到这里可以很好地工作,因为由于陈旧的闭包范围([] dep,我们只能从组件中读取 initial 道具和状态。在useEffect中。

How to omit function dependencies

  • 在效果内移动功能
  • 将功能移动到组件之外-(我们正在使用此功能)
  • 在渲染过程中调用函数,并让useEffect依赖于此值(纯计算函数)
  • 添加功能以实现效果并以useCallback作为最后包装方法

关于其他解决方案:

fetchBusinesses拉入useEffect()并不能真正帮助您,如果您访问其中的其他状态。埃斯林特仍然会抱怨:Codesandbox

我也将避免eslint详尽的说明而忽略评论。在对依赖项进行一些重构和大修时,很容易忘记它们。

答案 9 :(得分:1)

这些警告对于查找不一致更新的组件非常有用:https://reactjs.org/docs/hooks-faq.html#is-it-safe-to-omit-functions-from-the-list-of-dependencies

但是,如果要删除整个项目中的警告,可以将其添加到eslint配置中:

  {
  "plugins": ["react-hooks"],
  "rules": {
    "react-hooks/exhaustive-deps": 0
    }
  }

答案 10 :(得分:1)

该解决方案也是由react提供的,他们建议您使用useCallback,这将返回函数的备忘版本:

  

'fetchBusinesses'函数使useEffect Hook(在第NN行)的依赖关系在每个渲染上都改变。要解决此问题,请将“ fetchBusinesses”定义包装到其自己的useCallback()中。Hook react-hooks / exhaustive-deps

useCallback使用简便,因为它与useEffect具有相同的签名,不同之处在于useCallback返回一个函数。 看起来像这样:

 const fetchBusinesses = useCallback( () => {
        return fetch("theURL", {method: "GET"}
    )
    .then(// some stuff)
    .catch(// some error handling)
  }, [//deps])
  // We have a first effect thant uses fetchBusinesses
  useEffect(() => {
    // do things and then fetchBusinesses
    fetchBusinesses(); 
  }, [fetchBusinesses]);
   // We can have many effect thant uses fetchBusinesses
  useEffect(() => {
    // do other things and then fetchBusinesses
    fetchBusinesses();
  }, [fetchBusinesses]);

答案 11 :(得分:0)

实际上,当您使用钩子进行开发时,警告非常有用。但在某些情况下,它可能会刺穿您。尤其是当您不需要监听依赖项更改时。

如果您不想将fetchBusinesses放入钩子的依赖项中,则可以简单地将其作为参数传递给钩子的回调,并像这样将主fetchBusinesses设置为其默认值

useEffect((fetchBusinesses = fetchBusinesses) => {
   fetchBusinesses();
}, []);

这不是最佳实践,但在某些情况下可能会有用。

也如Shubnam所写,您可以添加以下代码以告诉ESLint忽略对钩子的检查。

// eslint-disable-next-line react-hooks/exhaustive-deps

答案 12 :(得分:0)

您可以删除第二个参数类型数组[],但是fetchBusinesses()也将在每次更新时被调用。您可以根据需要将IF语句添加到fetchBusinesses()实现中。

React.useEffect(() => {
  fetchBusinesses();
});

另一种方法是在组件外部实现fetchBusinesses()函数。只是不要忘记将任何依赖项参数传递给您的fetchBusinesses(dependency)调用(如果有)。

function fetchBusinesses (fetch) {
  return fetch("theURL", { method: "GET" })
    .then(res => normalizeResponseErrors(res))
    .then(res => res.json())
    .then(rcvdBusinesses => {
      // some stuff
    })
    .catch(err => {
      // some error handling
    });
}

function YourComponent (props) {
  const { fetch } = props;

  React.useEffect(() => {
    fetchBusinesses(fetch);
  }, [fetch]);

  // ...
}

答案 13 :(得分:0)

好吧,如果您想换个角度看,您只需要知道React具有非exhaustive-deps的选项是什么?您不应该在效果内使用闭包函数的原因之一是在每个渲染上,它将再次被重新创建/破坏。

因此,钩子中有多个React方法被认为是稳定且不会耗尽的,您无需将它们应用于useEffect依赖项,从而不会破坏react-hooks/exhaustive-deps的规则参与。例如,第二个返回变量useReduceruseState是一个函数。

const [,dispatch] = useReducer(reducer, {});

useEffect(() => {
    dispatch(); // non-exhausted, eslint won't nag about this
}, []);

因此,您可以将所有外部依赖项与当前依赖项一起共存于化简函数中。

const [,dispatch] = useReducer((current, update) => {
    const { foobar } = update;
    // logic

    return { ...current, ...update };
}), {});

const [foobar, setFoobar] = useState(false);

useEffect(() => {
    dispatch({ foobar }); // non-exhausted `dispatch` function
}, [foobar]);

答案 14 :(得分:0)

本文是使用钩子获取数据的不错的入门:https://www.robinwieruch.de/react-hooks-fetch-data/

本质上,请在useEffect中包含提取功能定义:

useEffect(() => {
  const fetchBusinesses = () => {
    return fetch("theUrl"...
      // ...your fetch implementation
    );
  }

  fetchBusinesses();
}, []);

答案 15 :(得分:-1)

仅在下一行禁用eslint;

useEffect(() => {
   fetchBusinesses();
// eslint-disable-next-line
}, [fetchBusinesses]);

答案 16 :(得分:-3)

在文件顶部添加此评论以禁用警告。

/* eslint-disable react-hooks/exhaustive-deps */