web-dev-qa-db-fra.com

React Hook Avertissements pour la fonction async utilisée dans useEffect: la fonction useEffect doit renvoyer une fonction de nettoyage ou rien

J'essayais l'exemple useEffect, comme ci-dessous:

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

et je reçois cet avertissement dans ma console. Mais le nettoyage est facultatif pour les appels asynchrones, je pense. Je ne sais pas pourquoi je reçois cet avertissement. Lier le bac à sable pour des exemples. https://codesandbox.io/s/24rj871r0penter image description here

36
RedPandaz

Je suggère de regarder Dan Abramov (un des créateurs de réaction) répond ici :

Je pense que vous compliquez les choses plus que nécessaire.

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

À plus long terme, nous découragerons cette tendance, car elle encourage les conditions de concurrence. Par exemple, tout peut arriver entre le début et la fin de votre appel et vous auriez pu obtenir de nouveaux accessoires. Au lieu de cela, nous recommanderons Suspense pour la récupération de données qui ressemblera davantage à

const response = MyAPIResource.read();

et aucun effet. Mais entre-temps, vous pouvez déplacer le contenu asynchrone vers une fonction distincte et l'appeler.

46
RTW

Lorsque vous utilisez une fonction asynchrone comme

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

il retourne une promesse et useEffect ne s'attend pas à ce que la fonction de rappel retourne Promise, mais attend que rien ne soit renvoyé ou qu'une fonction ne soit renvoyée.

Pour contourner cet avertissement, vous pouvez utiliser une fonction asynchrone à invocation automatique.

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

ou pour le rendre plus propre, vous pouvez définir une fonction et l'appeler

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 deuxième solution facilitera la lecture et vous aidera à rédiger le code permettant d'annuler les demandes précédentes si une nouvelle est déclenchée ou d'enregistrer la dernière réponse à la demande dans l'état

Codesandbox de travail

27
Shubham Khatri

Jusqu'à ce que React fournisse un meilleur moyen, vous pouvez créer un assistant, useEffectAsync.js:

import { useEffect } from 'react';


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

Maintenant, vous pouvez passer une fonction asynchrone:

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

J'ai lu cette question et je pense que la meilleure façon de mettre en œuvre useEffect n'est pas mentionnée dans les réponses. Disons que vous avez un appel réseau et que vous souhaitez faire quelque chose une fois que vous avez la réponse. Par souci de simplicité, enregistrons la réponse du réseau dans une variable d'état. On peut vouloir utiliser action/réducteur pour mettre à jour le magasin avec la réponse du réseau.

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

Référence => https://reactjs.org/docs/hooks-effect.html#tip-optimizing-performance-by-skipping-effects

0
Chiranjib