2 votes

Comment regrouper les appels React setState() à l'intérieur d'une fonction asynchrone ?

J'ai rencontré un problème où React ne regroupe pas les appels setState() multiples à l'intérieur d'une fonction asynchrone (React le fait habituellement automatiquement en dehors d'une fonction asynchrone). Cela signifie que lorsque mon code est exécuté, plusieurs appels setState() consécutifs entrent en conflit les uns avec les autres, le premier setState() entraîne la mise à jour du composant et le second setState() se produit avant que le composant n'ait eu le temps de remonter et je reçois l'erreur "Can't perform a React state update on an unmounted component". J'ai fait quelques recherches et j'ai découvert que c'est un comportement attendu, mais je n'ai rien trouvé sur la façon de résoudre ce problème.

Voici un exemple de code (qui ne s'exécute pas) pour visualiser ce que j'essaie de dire. Dans ce cas, les lignes setData(response.data) ; et setLoading(false) ; entrent en conflit et provoquent l'erreur.

J'ai besoin d'un moyen de rendre les deux appels atomiques.

import React, { useState } from "react";
import Button from "@material-ui/core/Button";

const Demo = () => {
  const [loading, setLoading] = useState(false);
  const [data, setData] = useState({});

  const fetchData = async () => {
    setLoading(true);
    const response = await callAPI();
    if (response.status === 200) {
      setData(response.data);
    }
    setLoading(false);
  };

  return (
    <div>
      <Button disabled={loading} onClick={() => fetchData()}>
        Fetch Data
      </Button>
      <p>{JSON.stringify(data)}</p>
    </div>
  );
};

export default Demo;

0voto

IAmVisco Points 165

Je pense que vous pouvez éviter cela en faisant du chargement une variable booléenne autonome au lieu d'une partie de l'état du composant. Une autre approche serait d'utiliser useEffect pour retarder l'appel de l'API jusqu'à ce que votre loading changement de variable. Cette réponse vous couvre. Et enfin, vous pouvez utiliser des composants de classe - setState() accepte un callback comme second paramètre - c'était la façon de faire des changements d'état atomiques avant que les hooks ne soient mis en place.

0voto

Robert Lewis Points 332

Vous pourriez regrouper vos états dans un objet d'état composite et le faire de la manière suivante...

import React, { useState } from "react";
import Button from "@material-ui/core/Button";

const Demo = () => {
  const [demoState, setDemoState] = useState({data: null, loading: false});

  const fetchData = () => {
    setDemoState({...demoState, loading: true});
    callAPI()
      .then( response => handleResponse(response)} );
  }

  const handleResponse = (response) => {
    if (response.status === 200) {
      setDemoState({data: response.data, loading: false});
    } else {
      setDemoState({...demoState, loading: false});
    }
  }

  return (
    <div>
      <Button disabled={loading} onClick={() => fetchData()}>
        Fetch Data
      </Button>
      <p>{JSON.stringify(demoState.data)}</p>
    </div>
  );
};

export default Demo;

0voto

Ehsan sarshar Points 112

Vous pouvez combiner le chargement et les données dans un même état. et aussi, au lieu de charger, utiliser l'état.

par exemple :

/** status possible values can be: "idle", "pending", "resolved", "rejected"*/

const [state, setState] = useState({status: "idle", data: null})

maintenant vous pouvez facilement changer le statut et sur cette base gérer différentes choses

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X