我应该如何删除“ React Hook useEffect缺少依赖项”警告?

时间:2019-07-22 14:50:42

标签: reactjs react-hooks

我有一个在useEffect中使用并单击事件的函数,然后引发警告“ React Hook useEffect缺少依赖项”,我应该如何删除警告?

// location is react router location
const Component = ({ location }) => {
  const [data, setData] = useState(null);

  const fetchData = () => {
    const { id } = parseLocation(location);
    fetchDataFromServer(id).then(data => setData(data));
  }

  useEffect(() => {
    fetchData();
  }, [location]);

  return (
    <div>
      {data}
      <button onClick={fetchData)}>reload</button>
    </div>
  );
}

然后我尝试此操作,但警告仍然存在

// location is react router location
const Component = ({ location }) => {
  const [data, setData] = useState(null);

  const fetchData = (l) => {
    // l is location
    const { id } = parseLocation(l);
    fetchDataFromServer(id).then(data => setData(data));
  }

  useEffect(() => {
    fetchData(location);
  }, [location]);

  return (
    <div>
      {data}
      <button onClick={() => fetchData(location)}>reload</button>
    </div>
  );
}

1 个答案:

答案 0 :(得分:1)

exhaustive-deps规则的重点是防止挂钩读取陈旧的道具或状态。问题在于,由于fetchData是在组件内定义的,就短绒而言,它可能正在访问陈旧的props或状态(通过闭包)。

一种解决方案是将fetchData从组件中拉出,并将其所需的所有内容传递给它:(已经通过了位置):

const fetchData = (l, setData) => {
  // l is location
  const { id } = parseLocation(l);
  fetchDataFromServer(id).then(data => setData(data));
}

const Component = ({ location }) => {
  const [data, setData] = useState(null);

  useEffect(() => {
    fetchData(location, setData);
  }, [location]);

  return (
    <div>
      {data}
      <button onClick={() => fetchData(location, setData)}>reload</button>
    </div>
  );
}

由于fetchData不在组件外部定义,因此linter知道它不会访问状态或道具,因此对于陈旧数据而言这不是问题。


不过,要清楚一点,从运行时的角度来看,您的原始解决方案是正确,因为fetchData不读取状态或道具-但是linter不知道。

您可以简单地禁用linter,但是以后以这种方式(如果fetchData曾经被修改过)不小心引入错误很容易。最好使用linter规则来验证正确性,即使这意味着对代码进行一些轻微的重组。


替代解决方案

利用闭包而不是将位置传递到fetchData的替代解决方案:

const Component = ({ location }) => {
  const [data, setData] = useState(null);

  const fetchData = useCallback(() => {
    // Uses location from closure
    const { id } = parseLocation(location);
    fetchDataFromServer(id).then(data => setData(data));

  // Ensure that location isn't stale
  }, [location]);

  useEffect(() => {
    fetchData();

  // Ensure that fetchData isn't stale
  }, [fetchData]);

  return (
    <div>
      {data}
      <button onClick={fetchData}>reload</button>
    </div>
  );
}

这种方法可以避免每次调用时将location传递到fetchData中。但是,使用这种方法时,务必确保避免过时的状态和道具。

如果您省略了[fetchData]的{​​{1}}深度,则效果只会运行一次,并且useEffect更改后将不会获取新数据。

但是,如果您在location的部门中有fetchData,但没有将useEffect包裹在fetchData中,则useCallback函数是每个渲染,这将导致fetchData运行每个渲染(这很糟糕)。

通过包装在useEffect中,useCallback函数只是fetchData发生变化时的一个新函数,这会导致location在适当的位置运行。