我应该如何删除警告 "React Hook useEffect has a missing dependency"?

How should i remove warning "React Hook useEffect has a missing dependency"?

我有一个函数在 useEffect 中使用并单击事件,然后抛出警告 "React Hook useEffect has a missing dependency",我应该如何删除警告?

// 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>
  );
}

exhaustive-deps 规则的要点是防止钩子读取陈旧的道具或状态。问题是,由于 fetchData 是在组件中定义的,就 linter 而言,它可能正在访问陈旧的 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]useEffect 的依赖,效果只会 运行 一次,并且 location 更改时不会获取新数据。

但是,如果您在 useEffect 的 deps 中有 fetchData,但不将 fetchData 包装在 useCallback 中,则 fetchData 函数是一个新函数每个渲染都起作用,这会导致每个渲染 useEffect 到 运行(这很糟糕)。

通过包装在 useCallback 中,只要 location 发生变化,fetchData 函数就只是一个新函数,这会导致 useEffect 变为 运行合适的点。