繁体   English   中英

为什么我的 useEffect 在 promise 被拒绝时无限运行,但在它被实现时只运行一次?

[英]Why my useEffect runs infinitely when promise gets rejected but only once when it gets fulfilled?

我正在尝试在 Axios 的帮助下从使用 JSON 服务器创建的模拟后端获取数据。 现在,当响应状态为 200 时,即成功获取数据时,我的成功变量的状态变为 true。 useEffect 只运行一次,消息 toast 只出现一次。 但是现在,当获取数据时出现错误时,useEffect 会无限运行,并且 toast 开始一个接一个地出现。 有人可以向我解释为什么会发生这种情况,我该如何解决这个问题?

下面是我写的代码。

import React, { useState, useEffect } from 'react';
import Loader from './../components/Loader';
import axios from 'axios';
import { toast } from 'react-toastify';

const PostsTD = () => {
  const [posts, setPosts] = useState([]);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState({
    status: false,
    message: '',
  });
  const [success, setSuccess] = useState(false);

  const getPosts = () => {
    axios
      .get('http://localhost:5050/posts')
      .then((res) => {
        setPosts(res.data);
        setLoading(false);
        if (res.status === 200) setSuccess(true);
      })
      .catch((error) => {
        setLoading(false);
        setError({ status: true, message: error.message });
      });
  };

  useEffect(() => {
    getPosts();

    if (error.status) {
      toast.error(error.message);
    }

    if (success) toast.success('Success!');
  }, [success, error]);

  if (loading) {
    return (
      <div className="px-28 text-center py-12">
        <Loader />
      </div>
    );
  }

  return (
    <div className="md:px-28 px-6">
      <h1 className="text-center font-extrabold text-gray-400 my-4 text-4xl">POSTS FETCHED USING AXIOS</h1>

      {posts && posts?.length > 0 ? (
        <div className="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 gap-3 py-2 px-3 bg-red-500">
          {posts?.map((post, idx) => {
            return (
              <div key={idx} className="p-3 bg-red-200 text-gray-900 rounded-md">
                <h2 className="font-semibold text-xl">{post.title}</h2>
                <p className="font-normal my-3">{post.body}</p>
              </div>
            );
          })}
        </div>
      ) : (
        <h1>NO posts to render</h1>
      )}
    </div>
  );
};

export default PostsTD;

  useEffect(() => {
    getPosts();

    if (error.status) {
      toast.error(error.message);
    }

    if (success) toast.success('Success!');
  }, [success, error]);

由于错误在依赖数组中,任何时候错误发生变化,这个效果都会运行。 所以错误发生了变化,这会导致你得到帖子,这会导致错误发生变化,等等。

我会将其拆分为单独的效果; 一个开始负载,另一个敬酒:

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

useEffect(() => {
  if (error.status) {
    toast.error(error.message);
  }

  if (success) toast.success('Success!');
}, [sucess, error]);

您的 useEffect 在其逻辑中具有依赖项和 getPosts 错误,因此,如果 getPosts 设置错误(因为它确实如此),它会创建一个无限循环。

useEffect => getPosts => setError => useEffect ...

您可以通过删除 useEffect 的错误依赖项来解决此问题,如果您仍想重新获取数据,我认为您应该直接在 setTimeout 中的 getPosts 中调用 getPosts。

正如其他人之前提到的,您的问题是您的useEffect触发另一个getPosts调用,而它只应该对成功或错误变量的变化做出反应。

在 effect 内部调用 API 时,请考虑以下几点:(#1 基于个人意见)

  1. 最好在效果内处理承诺(或制作 2 个单独的效果:1 个进行调用,1 个对成功/错误做出反应)
  2. 添加一个清理函数以在清理的情况下处理承诺取消( https://beta.reactjs.org/learn/synchronizing-with-effects#fetching-data

这就是我可能会如何构建它:

useEffect(() => {
    const controller = new AbortController();
    axios
        .get('http://localhost:5050/posts', {
            signal: controller.signal
        })
        .then((res) => {
            setPosts(res.data);
            setLoading(false);
            if (res.status === 200) setSuccess(true);
        })
        .catch((error) => {
            setLoading(false);
            setError({ status: true, message: error.message });
        });

    return () => {
        controller.abort();
    }
}, []);

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM