web-dev-qa-db-ja.com

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

コンソールにこの警告が表示されます。しかし、私が考える非同期呼び出しのクリーンアップはオプションです。この警告が表示される理由はわかりません。例としてサンドボックスをリンクします。 https://codesandbox.io/s/24rj871r0penter image description here

36
RedPandaz

私は ダン・アブラモフ(反応するクリエーターの1人)の答えをここに見ることをお勧めします

必要以上に複雑になっていると思います。

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

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

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

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

長期的には、競合状態を助長するため、このパターンは推奨されません。など-通話の開始と終了の間に何が発生する可能性があり、新しい小道具を手に入れることができました。代わりに、データフェッチにはSuspenseをお勧めします。

const response = MyAPIResource.read();

そして効果なし。ただし、その間に非同期のものを別の関数に移動して呼び出すことができます。

46
RTW

次のような非同期関数を使用する場合

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);
    }
}

promiseを返し、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();
}, []);

2番目の解決策は読みやすくし、新しい要求が発生した場合に以前の要求をキャンセルするか、状態の最新の要求応答を保存するためのコードを書くのに役立ちます

Working codesandbox

27
Shubham Khatri

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

私はこの質問を読み、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

0
Chiranjib