Estaba probando el ejemplo de useEffect
algo como a continuación:
useEffect(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); } }, []);
y recibo esta advertencia en mi consola. Pero creo que la limpieza es opcional para las llamadas asíncronas. No estoy seguro de por qué recibo esta advertencia. Vinculación de sandbox para ejemplos. https://codesandbox.io/s/24rj871r0p
Sugiero mirar la respuesta de Dan Abramov (uno de los mantenedores del núcleo 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 más largo plazo, desaconsejaremos este patrón porque fomenta las condiciones de carrera. Por ejemplo, cualquier cosa podría pasar 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 suspenso experimental aquí .
Si desea utilizar funciones externas con eslint.
function OutsideUsageExample({ userId }) { const [data, dataSet] = useState<any>(null) const fetchMyAPI = useCallback(async () => { let response = await fetch('api/data/' + userId) response = await response.json() dataSet(response) }, [userId]) // if userId changes, useEffect will run again useEffect(() => { fetchMyAPI() }, [fetchMyAPI]) return ( <div> <div>data: {JSON.stringify(data)}</div> <div> <button onClick={fetchMyAPI}>manual fetch</button> </div> </div> ) }
Si usa useCallback, mire el ejemplo de cómo funciona useCallback . caja de arena
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 autoinvocada.
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
Hasta que React proporcione una mejor manera, puede crear un asistente, useEffectAsync.js
:
import { useEffect } from 'react'; export default function useEffectAsync(effect, inputs) { useEffect(() => { effect(); }, inputs); }
Ahora puedes 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 una mala forma. Recurro a esto cuando sé que es seguro, pero siempre es de mala forma y al azar.
Suspense for Data Fetching , que aún es experimental, resolverá algunos de los casos.
En otros casos, puede modelar los resultados asincrónicos como eventos para que pueda agregar o quitar un agente de escucha según el ciclo de vida del componente.
O puede modelar los resultados asíncronos como un Observable para que pueda suscribirse y darse de baja en función del ciclo de vida del componente.