Saltar al contenido

React Hook Warnings para la función asíncrona en uso Efecto: la función useEffect debe devolver una función de limpieza o nada

Solución:

Sugiero ver la respuesta de Dan Abramov (uno de los mantenedores centrales de React) aquí:

Creo que lo estás complicando más de lo necesario.

function Example() {
  const [data, dataSet] = useState<any>(null)

  useEffect(() => {
    async function fetchMyAPI() {
      let response = await fetch('api/data')
      response = await response.json()
      dataSet(response)
    }

    fetchMyAPI()
  }, [])

  return <div>{JSON.stringify(data)}</div>
}

A largo plazo, desalentaremos este patrón porque fomenta las condiciones de carrera. Por ejemplo, cualquier cosa podría suceder entre el comienzo y el final de su llamada, y podría haber obtenido nuevos accesorios. En su lugar, recomendaremos Suspense para la obtención de datos, que se parecerá más a

const response = MyAPIResource.read();

y sin efectos. Pero mientras tanto, puede mover las cosas asíncronas a una función separada y llamarla.

Puedes leer más sobre el suspenso experimental aquí.


Si desea utilizar funciones en el exterior con eslint.

 function OutsideUsageExample() {
  const [data, dataSet] = useState<any>(null)

  const fetchMyAPI = useCallback(async () => {
    let response = await fetch('api/data')
    response = await response.json()
    dataSet(response)
  }, [])

  useEffect(() => {
    fetchMyAPI()
  }, [fetchMyAPI])

  return (
    <div>
      <div>data: {JSON.stringify(data)}</div>
      <div>
        <button onClick={fetchMyAPI}>manual fetch</button>
      </div>
    </div>
  )
}

Si va a utilizar useCallback, mire un ejemplo de cómo funciona useCallback. Salvadera.

import React, { useState, useEffect, useCallback } from "react";

export default function App() {
  const [counter, setCounter] = useState(1);

  // if counter is changed, than fn will be updated with new counter value
  const fn = useCallback(() => {
    setCounter(counter + 1);
  }, [counter]);

  // if counter is changed, than fn will not be updated and counter will be always 1 inside fn
  /*const fnBad = useCallback(() => {
      setCounter(counter + 1);
    }, []);*/

  // if fn or counter is changed, than useEffect will rerun
  useEffect(() => {
    if (!(counter % 2)) return; // this will stop the loop if counter is not even

    fn();
  }, [fn, counter]);

  // this will be infinite loop because fn is always changing with new counter value
  /*useEffect(() => {
    fn();
  }, [fn]);*/

  return (
    <div>
      <div>Counter is {counter}</div>
      <button onClick={fn}>add +1 count</button>
    </div>
  );
}

Cuando usas una función asíncrona como

async () => {
    try {
        const response = await fetch(`https://www.reddit.com/r/${subreddit}.json`);
        const json = await response.json();
        setPosts(json.data.children.map(it => it.data));
    } catch (e) {
        console.error(e);
    }
}

devuelve una promesa y useEffect no espera que la función de devolución de llamada devuelva Promise, sino que espera que no se devuelva nada o que se devuelva una función.

Como solución alternativa para la advertencia, puede utilizar una función asíncrona que se invoca automáticamente.

useEffect(() => {
    (async function() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    })();
}, []);

o para hacerlo más limpio, puede definir una función y luego llamarla

useEffect(() => {
    async function fetchData() {
        try {
            const response = await fetch(
                `https://www.reddit.com/r/${subreddit}.json`
            );
            const json = await response.json();
            setPosts(json.data.children.map(it => it.data));
        } catch (e) {
            console.error(e);
        }
    };
    fetchData();
}, []);

la segunda solución hará que sea más fácil de leer y lo ayudará a escribir código para cancelar solicitudes anteriores si se activa una nueva o guardar la última respuesta de solicitud en el estado

Códigos de trabajo y caja

Hasta que React proporcione una mejor manera, puede crear un ayudante, useEffectAsync.js:

import { useEffect } from 'react';


export default function useEffectAsync(effect, inputs) {
    useEffect(() => {
        effect();
    }, inputs);
}

Ahora puede pasar una función asíncrona:

useEffectAsync(async () => {
    const items = await fetchSomeItems();
    console.log(items);
}, []);

Actualizar

Si elige este enfoque, tenga en cuenta que es de mala forma. Recurro a esto cuando sé que es seguro, pero siempre es de mala forma y fortuito.

Suspense for Data Fetching, que aún es experimental, resolverá algunos de los casos.

En otros casos, puede modelar los resultados asíncronos como eventos para que pueda agregar o quitar un oyente según el ciclo de vida del componente.

O puede modelar los resultados asincrónicos como Observable para que pueda suscribirse y darse de baja según el ciclo de vida del componente.

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *