如何在 useEffect 中包含缺失的依赖项但防止函数的无限循环执行?

How to include missing dependency in useEffect but prevent infinite loop execution of function?

我正在开发一个相当简单的 React 功能组件,它从 URL 获取值 (tag) 并从 firestore correspondig 请求文档到 tag (inlc. firestore 查询光标与 加载更多 按钮一起使用)。整个组件由一个函数声明和一个调用该函数的useEffect钩子组成。

基本上该组件工作正常。但是有一个问题:ESLint 告诉我我需要将该函数作为 useEffect 挂钩的依赖项包括在内:

React Hook useEffect has a missing dependency: 'getNextImages'. Either include it or remove the dependency array react-hooks/exhaustive-deps

如果我将函数包含到依赖项数组中或完全删除依赖项数组,当然,组件最终会进入无限渲染循环。 我可以使用 // eslint-disable-next-line 跳过 ESLint 的依赖数组,但感觉不对。

我试图通过将 getNextImages 包装到 useCallback 钩子中来解决这个问题,但没有成功。但也许我做错了。我以前从来没有用过 useCallback

有没有人可以告诉我一些提示?

您会在 https://codesandbox.io/s/great-liskov-d12l3?file=/src/App.js 找到我的组件。或者在这里:

import { useEffect, useState } from "react";
import firebase from "firebase";
import { useParams } from "react-router-dom";

const GalleryTag = () => {
  //const [isPending, setIsPending] = useState(true);
  //const [galleryData, setGalleryData] = useState([]);
  //const [lastDocument, setLastDocument] = useState(null);
  //const [isLastPage, setIsLastPage] = useState(false);
  const numImagesPerPage = 20;
  const { tag } = useParams();

  const getNextImages = (lastDocument) => {
    let query;
    !lastDocument
      ? (query = firebase
          .firestore()
          .collectionGroup("images")
          .where("showinapp", "==", true)
          .where("tags", "array-contains", tag)
          .orderBy("createdate", "desc")
          .limit(numImagesPerPage)
          .get())
      : (query = firebase
          .firestore()
          .collectionGroup("images")
          .where("showinapp", "==", true)
          .where("tags", "array-contains", tag)
          .orderBy("createdate", "desc")
          .startAfter(lastDocument)
          .limit(numImagesPerPage)
          .get());
    query
      .then((data) => {
        setIsLastPage(data.empty);
        setIsPending(false);
        setLastDocument(data.docs[data.docs.length - 1]);
        setGalleryData((galleryData) => galleryData.concat(data.docs));
      })
      .catch((error) => console.error(error));
  };

  useEffect(() => {
    getNextImages();
  }, [tag]);

  return <>JSX goes here then</>;
};

export default GalleryTag;

尝试将您的 function 移至 useEffect 回调:

  
  useEffect(() => {
    const getNextImages = (lastDocument) => {
      let query;
      !lastDocument
        ? (query = firebase
            .firestore()
            .collectionGroup("images")
            .where("showinapp", "==", true)
            .where("tags", "array-contains", tag)
            .orderBy("createdate", "desc")
            .limit(numImagesPerPage)
            .get())
        : (query = firebase
            .firestore()
            .collectionGroup("images")
            .where("showinapp", "==", true)
            .where("tags", "array-contains", tag)
            .orderBy("createdate", "desc")
            .startAfter(lastDocument)
            .limit(numImagesPerPage)
            .get());
      query
        .then((data) => {
          setIsLastPage(data.empty);
          setIsPending(false);
          setLastDocument(data.docs[data.docs.length - 1]);
          setGalleryData((galleryData) => galleryData.concat(data.docs));
        })
        .catch((error) => console.error(error));
    };
    getNextImages();
  }, [tag]);

现在我得到了解决方案。它被称为 useCallback 钩子。

在玩完 useCallback 及其依赖数组后,我只需要将函数 getNextImages() 包装在这个钩子中并设置它的依赖项(在本例中为 tag)。

然后我可以将函数 getNextImages 设置为下面 useEffect 挂钩的依赖项……瞧:ESLint 现在很开心(当然还有构建过程)。重新渲染和无限执行循环也被阻止。

这是工作代码:

const getNextImages = useCallback(
    (lastDocument) => {
      let query;
      !lastDocument
        ? (query = firebase
            .firestore()
            .collectionGroup("images")
            .where("showinapp", "==", true)
            .where("tags", "array-contains", tag)
            .orderBy("createdate", "desc")
            .limit(numImagesPerPage)
            .get())
        : (query = firebase
            .firestore()
            .collectionGroup("images")
            .where("showinapp", "==", true)
            .where("tags", "array-contains", tag)
            .orderBy("createdate", "desc")
            .startAfter(lastDocument)
            .limit(numImagesPerPage)
            .get());
      query
        .then((data) => {
          setIsLastPage(data.empty);
          setIsPending(false);
          setLastDocument(data.docs[data.docs.length - 1]);
          setGalleryData((galleryData) => galleryData.concat(data.docs));
        })
        .catch((error) => console.error(error));
    },
    [tag]
  );

  useEffect(() => {
    getNextImages();
    setPageTitle(`Bilder zum Tag »${tag}«`);
    sendAnalytics("page_view", {
      page_title: `Bilder zum Tag »${tag}«`,
    });
  }, [getNextImages, tag]);