Estoy tratando de obtener datos de un backend simulado creado con el servidor JSON con la ayuda de Axios. Ahora, cuando el estado de respuesta es 200, es decir, los datos se obtuvieron con éxito, el estado de mi variable de éxito cambia a verdadero. El useEffect se ejecuta solo una vez y el mensaje brinda solo aparece una vez. Pero ahora, cuando hay un error al obtener los datos, useEffect se ejecuta infinitamente y el brindis comienza a aparecer sin parar uno tras otro. ¿Alguien puede explicarme por qué sucede esto y cómo puedo resolver este problema?
A continuación se muestra el código que he escrito.
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]);Dado que el error está en la matriz de dependencias, cada vez que cambie el error, este efecto se ejecutará. Entonces, el error cambia, lo que hace que obtenga las publicaciones, lo que hace que el error cambie, etc.
Dividiría esto en efectos separados; uno para dar inicio a la carga, y otro para hacer los brindis:
useEffect(() => { getPosts(); }, []); useEffect(() => { if (error.status) { toast.error(error.message); } if (success) toast.success('Success!'); }, [sucess, error]);Como otros han mencionado antes, su problema es que su useEffect desencadena otra llamada getPosts cuando se suponía que solo reaccionaría a un cambio en las variables de éxito o error.
Cuando realice llamadas a la API dentro de un efecto, tenga en cuenta lo siguiente: (el número 1 se basa en una opinión personal)
Así es como probablemente lo estructuraría:
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(); } }, []);Su useEffect tiene un error como dependencia y getPosts en su lógica, por lo tanto, si getPosts establece un error (como lo hace), crea un bucle infinito.
useEffect => getPosts => setError => useEffect ...
Puede resolver esto eliminando la dependencia de error de su useEffect, si aún desea recuperar datos, creo que debería llamar directamente a getPosts en getPosts en un setTimeout, por ejemplo.