使用useEffect React Hook时如何解决缺少依赖项警告?


174

使用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()。我确实在https://github.com/facebook/react/issues/14920找到了这个讨论,在这里可能的解决方案是You can always // eslint-disable-next-line react-hooks/exhaustive-deps if you think you know what you're doing.我不确定自己在做什么,所以我还没有尝试实现它。

我有这个当前设置,React钩子useEffect永远/无限循环连续运行,唯一的注释是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
      });
  };

Answers:


189

如果除了效果以外没有在其他地方使用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内部

要禁用该规则,您可以这样写

useEffect(() => {
   // other code
   ...

   // eslint-disable-next-line react-hooks/exhaustive-deps
}, []) 

14
我很好地使用了您概述的解决方案。由于设置不同,我在其他地方使用的另一个解决方案是useCallback()。因此,例如: const fetchBusinesses= useCallback(() => { ... }, [...])useEffect()看起来像这样:useEffect(() => { fetchBusinesses(); }, [fetchBusinesses]);
russ

1
@russ,您是对的,如果要将其传递给依赖项数组,则需要使用useCallback来记住fetchBusiness
Shubham Khatri

如果您显示将eslint-disable语句放在哪里,那将很好。我以为它会在useEffect之上
user210757 '19

1
使用// eslint-disable-next-line react-hooks/exhaustive-deps说明的棉短绒,你的代码是正确的就像是一个黑客。我希望他们会找到另一种解决方案,使
短毛绒

1
@TapasAdhikary,是的,您可以在useEffect中使用异步函数,您只需要以不同的方式编写它即可。请检查stackoverflow.com/questions/53332321/...
Shubham卡特里

75
./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错误,而是eslint(eslint-plugin-react-hooks)警告。

它告诉您钩子依赖于function fetchBusinesses,因此您应该将其作为依赖传递。

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

如果在组件中声明函数,则可能会导致每个渲染调用函数:

const Component = () => {
  /*...*/

  //new function declaration every render
  const fetchBusinesses = () => {
    fetch('/api/businesses/')
      .then(...)
  }

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

  /*...*/
}

因为每次使用新引用重新声明函数

正确的方法是:

const Component = () => {
  /*...*/

  // keep function reference
  const fetchBusinesses = useCallback(() => {
    fetch('/api/businesses/')
      .then(...)
  }, [/* additional dependencies */]) 

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

  /*...*/
}

或只是在定义功能 useEffect

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


14
这导致一个新的错误Line 20: The 'fetchBusinesses' function makes the dependencies of useEffect Hook (at line 51) change on every render. Move it inside the useEffect callback. Alternatively, wrap the 'fetchBusinesses' definition into its own useCallback() Hook
鲁斯

1
解决方案很好,如果您在函数上修改了另一个状态,则必须添加依赖项以避免发生另一个意外行为
cesarlarsson

55

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

useEffect(fetchBusinesses, [])

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


编辑02/21/2020

仅出于完整性考虑:

1.使用函数作为useEffect回调(如上所述)

useEffect(fetchBusinesses, [])

2.声明内部功能 useEffect()

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

3.与 useCallback()

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

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

4.禁用eslint的警告

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

2
我爱你...答案很完整!
Nick09

8

该解决方案也由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]);


1

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

本质上,在内部包含fetch函数定义useEffect

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

  fetchBusinesses();
}, []);

1

您可以删除第二个参数类型数组,[]fetchBusinesses()每次更新都会调用。您可以根据需要IFfetchBusinesses()执行中添加一条语句。

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]);

  // ...
}

0

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

如果您不想放入fetchBusinesses挂钩的依赖项,则只需将其作为参数传递给挂钩的回调,然后将main设置fetchBusinesses为其的默认值,如下所示

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

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

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

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

0

我只是想运行[ 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 in ),我们仍然只能从组件中读取初始道具和状态。[]useEffect

如何忽略函数依赖

  • 在效果内移动功能
  • 将功能移到组件之外-(我们正在使用这一功能)
  • 渲染期间调用函数,并让其useEffect依赖于此值(纯计算函数)
  • 添加功能以影响效果并useCallback最终包装

关于其他解决方案:

如果您访问其中的其他状态,则拉入fetchBusinesses内部useEffect()并没有真正的帮助。eslint仍然会抱怨:Codesandbox

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


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

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


0

你这样尝试

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


-4

只需在下一行禁用eslint即可;

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

这样,您就可以像安装组件一样使用它(称为一次)

更新

要么

const fetchBusinesses = useCallback(() => {
 // your logic in here
 }, [someDeps])

useEffect(() => {
   fetchBusinesses();
// no need to skip eslint warning
}, [fetchBusinesses]); 

每当某些Dept更改时,都会调用fetchBusinesses


而不是禁用它,只需执行以下操作:[fetchBusinesses]将自动删除警告,并为我解决了该问题。
rotimi-best

7
@RotimiBest-这样做会导致无限重渲染,如问题所述
user210757

实际上,我前一段时间在我的一个项目中以这种方式完成了此操作,但并没有产生无限循环。我会再次检查。
rotimi-best

@ user210757等待,但是为什么会导致无限循环,这与您从服务器获取数据后不像在设置状态一样。如果要更新状态,则只需在调用函数之前编写一个if条件useEffect,即可检查状态是否为空。
rotimi最佳

@ rotimi-best自从我发表评论以来一直很不错,但是我会说函数每次都会重新创建,因此永远不会一样,因此它将始终重新呈现,除非您进入useEffect主体或useCallback
user210757
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.