React Hook Warnings для функции async в useEffect: useEffect функция должна возвращать функцию очистки или ничего

Я пытался использовать пример useEffect что-то вроде ниже:

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);
    }
}, []);

Ответы

Ответ 1

Предлагаю посмотреть ответ Дана Абрамова (одного из ведущих разработчиков React) здесь:

Я думаю, вы делаете это более сложным, чем нужно.

export default function Example() { 
    const [data, dataSet] = useState(false)

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

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

  return <div>{data}</div>
}

В долгосрочной перспективе мы будем препятствовать этой модели, потому что она стимулирует условия гонки. Например, между началом и окончанием вызова может произойти все, и вы могли бы получить новые реквизиты. Вместо этого мы рекомендуем Suspense для извлечения данных, который будет больше похож на

const response = MyAPIResource.read();

и никаких эффектов. Но в то же время вы можете переместить асинхронный материал в отдельную функцию и вызвать ее.

Ответ 2

Когда вы используете асинхронную функцию типа

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 не ожидает, что функция обратного вызова вернет Promise, скорее он ожидает, что ничего не возвращается или функция не возвращается.

В качестве обходного пути для предупреждения вы можете использовать самозапускающуюся асинхронную функцию.

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();
}, []);

Второе решение облегчит чтение и поможет вам написать код для отмены предыдущих запросов при запуске нового или сохранения последнего ответа на запрос в состоянии

Рабочие коды и коробка

Ответ 3

Пока 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);
}, []);

Ответ 4

Я прочитал этот вопрос и чувствую, что лучший способ реализации 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