¿Cómo llamar a una función asíncrona dentro de useEffect() en React?

5 minutos de lectura

Avatar de usuario de Yassine Layachi
Yassine Layachi

Me gustaría llamar a una función asíncrona y obtener el resultado de mi UseEffect.

Los ejemplos de fetch api que encontré en Internet se realizan directamente en la función useEffect. Si mi URL cambia, debo parchear todas mis búsquedas.

Cuando lo intenté, recibí un mensaje de error.

Este es mi código.


    async function getData(userId) {
        const data = await axios.get(`http://url/api/data/${userId}`)
            .then(promise => {
                return promise.data;
            })
            .catch(e => {
                console.error(e);
            })
            return data;
    }
    

    function blabla() {
        const [data, setData] = useState(null);
    
        useEffect(async () => {
            setData(getData(1))
        }, []);
    
        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }

index.js:1375 Advertencia: una función de efecto no debe devolver nada más que una función, que se usa para la limpieza. Parece que escribiste useEffect(async () => …) o devolviste una Promesa. En su lugar, escribe la función asíncrona dentro de tu efecto y llámala inmediatamente:

useEffect(() => {
  async function fetchData() {
    // You can await here
    const response = await MyAPI.getData(someId);
    // ...
  }
  fetchData();
}, [someId]); // Or [] if effect doesn't need props or state

  • Si una función devuelve una promesa, puede await o .then(...)no ambos.

    – Omagario

    1 de julio de 2019 a las 15:36

  • Supongo que esto responde la pregunta, pero de ninguna manera esto realmente resuelve el problema. Efectivamente, está comenzando una promesa que podría terminar en cualquier momento. Si su trabajo asíncrono no está relacionado con el ciclo de vida de los componentes, está bien. Pero de lo contrario, se encontrará con problemas y errores de representación difíciles de depurar. No tengo suficiente experiencia con React para estar seguro, pero siento que esto también interfiere con el sistema de dependencia de React.

    – Romain Vicente

    20 de abril de 2021 a las 8:44


  • Consulte también las advertencias de React Hook para la función asíncrona en useEffect

    – Bergi

    11 de noviembre de 2021 a las 19:32


Avatar de usuario de Fraction
Fracción

Cree una función asíncrona dentro de su efecto que espere el getData(1) resultado luego llamar setData():

useEffect(() => {
  const fetchData = async () => {
     const data = await getData(1);
     setData(data);
  }

  fetchData();
}, []);

  • ¿Por qué esto es diferente a si la función asíncrona se definiera fuera del enlace useEffect?

    –Leland Reardon

    17 de agosto de 2021 a las 15:43


  • @LelandReardon Si desea definir la función asíncrona fuera de la useEffect gancho, tienes que agregarlo a la lista de dependencias de useEffect y envolver su definición en un useCallback con las dependencias necesarias para evitar llamadas innecesarias, para obtener más información, consulte la documentación de reacción aquí

    – Fracción

    24 de agosto de 2021 a las 11:34

Si lo está invocando de inmediato, es posible que desee usarlo como una función anónima:

useEffect(() => {

  (async () => {
     const data = await getData(1);
     setData(data);
  })();

}, []);

  • ¿Cuales son los beneficios?

    – Remi

    6 de octubre de 2020 a las 11:58

  • @Remi La función se invocará de inmediato y no se usará en ningún otro lugar, por lo que no necesita un nombre ni ninguna predefinición

    – hsusanoo

    6 oct 2020 a las 12:10

  • ¿Qué hay de cancelar la solicitud si el componente se desmonta?

    – Remi

    6 oct 2020 a las 12:15

  • @Remi esto no está relacionado con la pregunta de OP, puede hacer esta pregunta en un hilo separado, ya que puede tener diferentes implementaciones, la mayoría de las cuales no están relacionadas con si usa un asíncrono anónimo o uno predefinido para obtener datos

    – hsusanoo

    6 de octubre de 2020 a las 12:23

  • Caso extremo. Si no lo está cancelando, React establecerá un estado incluso cuando este componente esté desmontado. Creo que algunas bibliotecas de prueba incluso se quejarán. Por lo tanto este ejemplo no es recomendable persé.

    – Remi

    6 oct 2020 a las 14:45

avatar de usuario de zilijonas
zilijonas

Sería mejor si hiciera lo que sugiere la advertencia: llame a la función asíncrona dentro del efecto.

    function blabla() {
        const [data, setData] = useState(null);

        useEffect(() => {
            axios.get(`http://url/api/data/1`)
             .then(result => {
                setData(result.data);
             })
             .catch(console.error)
        }, []);

        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }

Si desea mantener la función api fuera del componente, también puede hacer esto:

    async function getData(userId) {
        const data = await axios.get(`http://url/api/data/${userId}`)
            .then(promise => {
                return promise.data;
            })
            .catch(e => {
                console.error(e);
            })
            return data;
    }


    function blabla() {
        const [data, setData] = useState(null);

        useEffect(() => {
            (async () => {
                const newData = await getData(1);
                setData(newData);
            })();
        }, []);

        return (
            <div>
                this is the {data["name"]}
            </div>
        );
    }

  • Gracias por la respuesta. Desafortunadamente, esto es exactamente lo que no quiero hacer. Si mi URL cambia, debo parchearla en todos los archivos que busqué. Para la escalabilidad, estoy tratando de hacer algo más como el primer código que publiqué.

    – Yassine Layachi

    1 julio 2019 a las 16:00

  • Podrías usar variables de entorno

    – Monstar

    22 de marzo de 2022 a las 21:28

Avatar de usuario de YingYang
Ying Yang

Desde getData devuelve una Promesa, podrías simplemente usar .then. En su caso, esto es mucho más simple que escribir una función asíncrona y llamarla directamente.

Además, desde axios.get ya devuelve una Promesa, tu getData la función no necesita ser marcada async. Esta es una versión simplificada y funcional de su código:

function getData(userId) {
    return axios.get(`http://url/api/data/${userId}`)
        .then(promise => promise.data)
        .catch(e => {
            console.error(e);
        });
}


function blabla() {
    const [data, setData] = useState(null);

    useEffect(async () => {
        getData(1).then(setData);
    }, []);

    return (
        <div>
            this is the {data["name"]}
        </div>
    );
}

avatar de usuario de brunettdan
brunettdan

El componente puede desmontarse o volver a renderizarse con diferentes someId antes de que se resuelva await:

const unmountedRef = useRef(false);
useEffect(()=>()=>(unmountedRef.current = true), []);

useEffect(() => {
  const effectStale = false; // Don't forget ; on the line before self-invoking functions
  (async function() {
    // You can await here
    const response = await MyAPI.getData(someId);

    /* Component has been unmounted. Stop to avoid
       "Warning: Can't perform a React state update on an unmounted component." */
    if(unmountedRef.current) return;

    /* Component has re-rendered with different someId value
       Stop to avoid updating state with stale response */
    if(effectStale) return;

    // ... update component state
  })();
  return ()=>(effectStale = true);
}, [someId]);

Considere usar Suspenso para los datos que deben cargarse antes de montar el componente.

Avatar de usuario de JamesK
jamesk

Todavía puede definir la función asíncrona fuera del gancho y llamarla dentro del gancho.

const fetchData = async () => {
   const data = await getData(1);
   setData(data);
}

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

¿Ha sido útil esta solución?