useEffect 警告缺少依赖项会导致无限循环

时间:2021-02-04 22:03:44

标签: reactjs use-effect

以下代码完美运行:

import { useState, useEffect } from 'react';

const Main = () => {

  const [ form, setForm ] = useState({
    error: {},
    data: {}
  });

  useEffect( () => {
    async function fetchData() {
      const promise = await fetch(`test.json`);
      const result = await promise.json();
      const newForm = {...form};
      newForm.data = result;
      setForm(newForm);
      console.log('executed');
    }
    fetchData();
  }, []);  // *** I will speak to this [] second argument shortly in question below

  return (
    <div>
      <p>{Object.keys(form.data).length}</p>
    </div>
  );
};

它所做的只是在组件安装上,获取一个包含 test.json 内容的 {"data":"hello"} 文件。这完美地工作并且做我想要的。

但是,在我的控制台中,我看到编译器抱怨此消息 Line 20:6: React Hook useEffect has a missing dependency: 'form'. Either include it or remove the dependency array react-hooks/exhaustive-deps。当我添加 [ form ] 作为 useEffect 的第二个参数时,或者如果我从 [] 中删除 useEffect 第二个参数,那么 useEffect 会进入无限循环。< /p>

为什么我的编译器会警告我出现问题并建议导致无限循环的操作?

1 个答案:

答案 0 :(得分:1)

此错误/警告是由您的 linter 造成的。

linter 规则假定您遗漏了依赖项数组中 useEffect 外部的变量,这会导致意外结果。

您可以禁用 lint 规则:

  1. 线
useEffect(() => {

}, []); // eslint-disable-line react-hooks/exhaustive-deps
  1. 文件的其余部分
/* eslint-disable react-hooks/exhaustive-deps */
useEffect(() => {

}, []);
  1. 使用 eslintrc 文件的所有文件。

如果您不想禁用规则,您可以切换到使用 setState 回调语法,该语法提供当前状态作为参数。

import { useState, useEffect } from 'react';

const Main = () => {

  const [ form, setForm ] = useState({
    error: {},
    data: {}
  });

  useEffect( () => {
    async function fetchData() {
      const promise = await fetch(`test.json`);
      const result = await promise.json();
      setForm(currentForm => {
        const newForm = {...currentForm};
        newForm.data = result;
        return newForm;
      });
      console.log('executed');
    }
    fetchData();
  }, []);

  return (
    <div>
      <p>{Object.keys(form.data).length}</p>
    </div>
  );
};

这消除了在 useEffect 中包含 form 的需要。

至于 linter 可能认为这是一个问题的原因,请看以下示例:

export default function App() {
  const [data, setData] = useState({ a: 'Initial Value', b: null });
  const [control, setControl] = useState({ a: "Initial Value", b: null });

  useEffect(() => {
    const asyncFunc = () => {
        new Promise(resolve => {
          setTimeout(() => resolve(true), 2000) 
        })
        .then(() => {
          // The value of "data" will be the initial value from 
          // when the useEffect first ran.
          setData({...data, b: 'Async Updated'});

          // The value of "current" wille be the current value of
          // the "control" state.
          setControl(current => ({ ...current, b: "Async Updated" }));
        })
    };

    asyncFunc();
    
    // Update the data state while the async function has not
    // yet been completed.
    setData({ a: 'Changed Value', b: null });
    
    // Update the control state while the async function has not
    // yet been completed.
    setControl({ a: "Changed Value", b: null });
  }, []);

  // The data value will swap to "Changed Value" and then back 
  // to "Initial Value" (unexpected) once the async request is
  // complete.
  // As the control used the current value provided by the 
  // callback it is able to avoid this issue. 
  return (
    <div>
      Data:
      <pre>{JSON.stringify(data, null, "\t")}</pre>
      Control:
      <pre>{JSON.stringify(control, null, "\t")}</pre>
    </div>
  );
};

您可以在此处运行此示例:https://stackblitz.com/edit/react-hmfddo