Предлагаю взглянуть на ответ Дэна Абрамова (одного из разработчиков ядра React) :
Я думаю, вы усложняете это дело, чем должно быть.
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>
}
В более долгосрочной перспективе мы не будем поощрять этот шаблон, потому что он поощряет гоночные условия. Например, между началом и окончанием звонка могло произойти что угодно, и вы могли получить новые реквизиты. Вместо этого мы рекомендуем Suspense для выборки данных, которая будет больше похожа на
const response = MyAPIResource.read();
и никаких эффектов. Но пока вы можете переместить асинхронный материал в отдельную функцию и вызвать ее.
Вы можете узнать больше об экспериментальном саспенсе здесь .
Если вы хотите использовать внешние функции с 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>
)
}
С useCallback useCallback . Песочница .
import React, { useState, useEffect, useCallback } from "react";
export default function App() {
const [counter, setCounter] = useState(1);
const fn = useCallback(() => {
setCounter(counter + 1);
}, [counter]);
useEffect(() => {
if (!(counter % 2)) return;
fn();
}, [fn, counter]);
return (
<div>
<div>Counter is {counter}</div>
<button onClick={fn}>add +1 count</button>
</div>
);
}
useEffect(() => { let unmounted = false promise.then(res => { if (!unmounted) { setState(...) } }) return () => { unmounted = true } }, [])
eslint
просит меня сделатьfetchMyAPI()
как зависимость от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); } }
он возвращает обещание и
useEffect
не ожидает, что функция обратного вызова вернет обещание, скорее он ожидает, что ничего не будет возвращено или функция будет возвращена.В качестве обходного пути для предупреждения вы можете использовать самозапускающуюся асинхронную функцию.
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); } })(); }, []);
или, чтобы сделать его более чистым, вы можете определить функцию, а затем вызвать ее
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(); }, []);
второе решение упростит чтение и поможет вам написать код для отмены предыдущих запросов, если запущен новый, или сохранить последний ответ на запрос в состоянии
Рабочие коды
источник
useEffect
вы можете вернуть функцию для очистки, например, для отмены подписки на события. Когда вы используете функцию async, вы не можете ничего вернуть, потомуuseEffect
что не будете ждать результата,Пока React не предоставит лучший способ, вы можете создать помощника
useEffectAsync.js
:import { useEffect } from 'react'; export default function useEffectAsync(effect, inputs) { useEffect(() => { effect(); }, inputs); }
Теперь вы можете передать асинхронную функцию:
useEffectAsync(async () => { const items = await fetchSomeItems(); console.log(items); }, []);
источник
useAsyncEffect
виде, в котором вы ее написали, может легко ввести кого-то в заблуждение, если он вернет функцию очистки из своего асинхронного эффекта, она будет запущена в подходящее время. Это могло привести к утечкам памяти или еще более серьезным ошибкам, поэтому мы решили побудить людей провести рефакторинг своего кода, чтобы сделать «шов» асинхронных функций, взаимодействующих с жизненным циклом React, более заметным, а поведение кода в результате, надеюсь, более осознанным и правильным.Я прочитал этот вопрос и считаю, что лучший способ реализовать useEffect не упоминается в ответах. Допустим, у вас есть сетевой вызов, и вы хотите что-то предпринять, когда получите ответ. Для простоты сохраним ответ сети в переменной состояния. Можно использовать действие / редуктор для обновления хранилища ответом сети.
const [data, setData] = useState(null); /* This would be called on initial page load */ useEffect(()=>{ fetch(`https://www.reddit.com/r/${subreddit}.json`) .then(data => { setData(data); }) .catch(err => { /* perform error handling if desired */ }); }, []) /* This would be called when store/state data is updated */ useEffect(()=>{ if (data) { setPosts(data.children.map(it => { /* do what you want */ })); } }, [data]);
Ссылка => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects
источник
Для других читателей ошибка может возникать из-за отсутствия скобок, заключающих асинхронную функцию:
Учитывая асинхронную функцию initData
async function initData() { }
Этот код приведет к вашей ошибке:
useEffect(() => initData(), []);
Но этот не будет:
useEffect(() => { initData(); }, []);
(Обратите внимание на скобки вокруг initData ()
источник
Здесь можно использовать оператор void .
Вместо того:
React.useEffect(() => { async function fetchData() { } fetchData(); }, []);
или
React.useEffect(() => { (async function fetchData() { })() }, []);
можно было написать:
React.useEffect(() => { void async function fetchData() { }(); }, []);
Он немного чище и красивее.
Асинхронные эффекты могут вызвать утечку памяти, поэтому важно выполнить очистку при отключении компонента. В случае выборки это могло бы выглядеть так:
function App() { const [data, setData] = React.useState([]) React.useEffect(() => { const abortController = new AbortController() ;(async function fetchData() { try { const url = "https://jsonplaceholder.typicode.com/todos/1" const response = await fetch(url, { signal: abortController.signal, }) setData(await response.json()) } catch (error) { console.log("error", error) } })() return abortController.abort // cancel pending fetch request on component unmount }, []) return <pre>{JSON.stringify(data, null, 2)}</pre> }
источник
пытаться
const MyFunctionnalComponent: React.FC = props => { useEffect(() => { // Using an IIFE (async function anyNameFunction() { await loadContent(); })(); }, []); return <div></div>; };
источник