reactjs 如何使用useEffect和useCallback摆脱这个无限循环?

ogq8wdun  于 2023-02-04  发布在  React
关注(0)|答案(2)|浏览(201)

我想立即加载第一注解释(使用useEffect),然后在按下“加载更多”按钮时加载其他页面。
问题是我当前的设置会导致无限循环(由对comments的依赖性导致)。
如果我从useEffect依赖项列表中删除fetchNextCommentsPage函数,所有功能似乎都正常工作,但EsLint抱怨缺少依赖项。

const [comments, setComments] = useState<CommentModel[]>([]);
    const [commentsLoading, setCommentsLoading] = useState(true);
    const [commentsLoadingError, setCommentsLoadingError] = useState(false);

    const [paginationEnd, setPaginationEnd] = useState(false);

    const fetchNextCommentsPage = useCallback(async function () {
        try {
            setCommentsLoading(true);
            setCommentsLoadingError(false);
            const continueAfterId = comments[comments.length - 1]?._id;
            const response = await BlogApi.getCommentsForBlogPost(blogPostId, continueAfterId);
            setComments([...comments, ...response.comments]);
            setPaginationEnd(response.paginationEnd);
        } catch (error) {
            console.error(error);
            setCommentsLoadingError(true);
        } finally {
            setCommentsLoading(false);
        }
    }, [blogPostId, comments])

    useEffect(() => {
        fetchNextCommentsPage();
    }, [fetchNextCommentsPage]);
lymnna71

lymnna711#

永远不要把你想改变的状态放在依赖列表中,因为这会引发无限循环问题。解决这个问题的常用方法是使用setState www.example.com的回调函数https://reactjs.org/docs/react-component.html#setstate。
如果你想让你的效果只被触发一次,那么在第一次加载后添加一些不会改变的东西。如果你想在按下按钮时加载更多的效果,那么只需要改变你的效果的依赖关系,用新的依赖关系值再次运行你的效果。

const [comments, setComments] = useState<CommentModel[]>([]);
    const [commentsLoading, setCommentsLoading] = useState(true);
    const [commentsLoadingError, setCommentsLoadingError] = useState(false);
    const [continueAfterId, setContinueAfterId] = useState(null)

    const [paginationEnd, setPaginationEnd] = useState(false);

    const fetchNextCommentsPage = useCallback(async function () {
        try {
            setCommentsLoading(true);
            setCommentsLoadingError(false);
            const response = await BlogApi.getCommentsForBlogPost(blogPostId, continueAfterId);
            setComments(previousState => [...previousState, ...response.comments]);
            setPaginationEnd(response.paginationEnd);
        } catch (error) {
            console.error(error);
            setCommentsLoadingError(true);
        } finally {
            setCommentsLoading(false);
        }
    }, [blogPostId, continueAfterId]);

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

    const onButtonPressed = useCallback(() => {
        // continueAfterId is one of the dependencies of fetchNextCommentsPage so it will change `fetchNextCommentsPage`, hence trigger the effect
        setContinueAfterId(comments[comments.length - 1]?._id)
    }, [comments])
ekqde3dh

ekqde3dh2#

感谢@ o-minh-h t的回答,我改进了它,把continueAfterId作为参数传递,而不是把它保存在另一种状态(我认为这更直观):

const [comments, setComments] = useState<CommentModel[]>([]);
const [commentsLoading, setCommentsLoading] = useState(true);
const [commentsLoadingError, setCommentsLoadingError] = useState(false);

const [paginationEnd, setPaginationEnd] = useState(false);

const fetchNextCommentsPage = useCallback(async function (continueAfterId?: string) {
    try {
        setCommentsLoading(true);
        setCommentsLoadingError(false);
        const response = await BlogApi.getCommentsForBlogPost(blogPostId, continueAfterId);
        setComments(existingComments => [...existingComments, ...response.comments]);
        setPaginationEnd(response.paginationEnd);
    } catch (error) {
        console.error(error);
        setCommentsLoadingError(true);
    } finally {
        setCommentsLoading(false);
    }
}, [blogPostId]);

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

然后在我的加载更多按钮的onClick:

<Button
   variant="outline-primary"
   onClick={() => fetchNextCommentsPage(comments[comments.length - 1]?._id)}>
    Load more comments
</Button>

相关问题