À quoi sert la syntaxe fonctionnelle de setState dans les composants fonctionnels React? [dupliquer]

Dec 13 2020

on parle de composants fonctionnels ayant useState

Disons

const [age, setAge] = useState(0)

disons maintenant que lors de la mise à jour, ageje dois utiliser le précédentage

Les documents React mentionnent quelque chose appelé MISES À JOUR FONCTIONNELLES où vous pouvez passer une fonction et l'argument à cela sera la valeur précédente de l'état, par exemple.

setState((previousAge) => previousAge + 1)

pourquoi ai-je besoin de faire ça alors que je peux juste faire

setState(previousAge + 1)

quels sont les avantages de l'utilisation fonctionnelle setState,

Je sais que dans les composants basés sur les classes, il y avait quelque chose appelé le traitement par lots des mises à jour d'état de manière fonctionnelle, mais je ne trouve rien de tel dans la documentation des composants fonctionnels.

Réponses

2 ehab Dec 14 2020 at 01:38

Ils ne sont pas les mêmes, si votre mise à jour dépend d'une valeur précédente trouvée dans l'état, vous devez utiliser la forme fonctionnelle. Si vous n'utilisez pas la forme fonctionnelle dans ce cas, votre code se cassera un jour.

Pourquoi ça casse et quand

Les composants fonctionnels React ne sont que des fermetures, la valeur d'état que vous avez dans la fermeture peut être obsolète - ce que cela signifie est que la valeur à l'intérieur de la fermeture ne correspond pas à la valeur qui est dans l'état React pour ce composant, cela peut se produire dans le cas suivants:

1- Opérations asynchrones ( dans cet exemple, cliquez sur ajout lent, puis cliquez plusieurs fois sur le bouton d'ajout, vous verrez plus tard que l'état a été réinitialisé à ce qui était à l'intérieur de la fermeture lorsque le bouton d'ajout lent a été cliqué)

const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <>
      <p>counter {counter} </p>
      <button
        onClick={() => {
          setCounter(counter + 1);
        }}
      >
        immediately add
      </button>
      <button
        onClick={() => {
          setTimeout(() => setCounter(counter + 1), 1000);
        }}
      >
        Add
      </button>
    </>
  );
};

2- Lorsque vous appelez la fonction de mise à jour plusieurs fois dans la même fermeture

const App = () => {
  const [counter, setCounter] = useState(0);

  return (
    <>
      <p>counter {counter} </p>
      <button
        onClick={() => {
          setCounter(counter + 1);
          setCounter(counter + 1);
        }}
      >
        Add twice
      </button>
   
    </>
  );
}
1 HeroWanders Dec 13 2020 at 23:59

Des problèmes peuvent survenir en fonction de la vitesse / fréquence à laquelle votre passeur est appelé.

Si vous utilisez la méthode simple en obtenant la valeur de la fermeture, les appels suivants entre deux rendus peuvent ne pas avoir l'effet souhaité.

Un exemple simple:

function App() {
    const [counter, setCounter] = useState(0);
    const incWithClosure = () => {
        setCounter(counter + 1);
    };
    const incWithUpdate = () => {
        setCounter(oldCounter => oldCounter + 1);
    };

    return (<>
        <button onClick={_ => { incWithClosure(); incWithClosure(); }}>
            Increment twice using incWithClosure
        </button>
        <button onClick={_ => { incWithUpdate(); incWithUpdate(); }}>
            Increment twice using incWithUpdate
        </button>
        <p>{counter}</p>
    </>);
}

Les deux boutons appellent deux fois l'une des méthodes d'incrémentation. Mais on observe:

  • Le premier bouton n'incrémentera le compteur que de 1
  • Le deuxième bouton incrémentera le compteur de 2, ce qui est probablement le résultat souhaité.

Quand cela peut-il arriver?

  • Évidemment, si incWithClosureest appelé plusieurs fois immédiatement après l'autre
  • Si des tâches asynchrones sont impliquées, cela peut facilement se produire (voir ci-dessous)
  • Peut-être que si React a beaucoup de travail à faire, ses algorithmes de planification peuvent décider de gérer plusieurs clics très rapides en utilisant le même gestionnaire d'événements

Exemple de travail asynchrone (simulation du chargement d'une ressource):

function App() {
  const [counter, setCounter] = useState(0);
  const incWithClosureDelayed = () => {
    setTimeout(() => {
      setCounter(counter + 1);
    }, 1000);
  };
  const incWithUpdateDelayed = () => {
    setTimeout(() => {
      setCounter((oldCounter) => oldCounter + 1);
    }, 1000);
  };

  return (
    <>
      <button onClick={(_) => incWithClosureDelayed()}>
        Increment slowly using incWithClosure
      </button>
      <button onClick={(_) => incWithUpdateDelayed()}>
        Increment slowly using incWithUpdate
      </button>
      <p>{counter}</p>
    </>
  );
}

Cliquez deux fois sur le premier bouton (en moins d'une seconde) et observez que le compteur ne s'incrémente que de 1. Le deuxième bouton a le bon comportement.

slebetman Dec 13 2020 at 23:26

Parce que si vous ne vous ne vous trouverez à un moment donné que vous obtenez une ancienne valeur pour age. Le problème est que parfois ce que vous suggérez fonctionnera. Mais parfois, ce ne sera pas le cas. Il se peut que votre code actuel ne soit pas interrompu aujourd'hui, mais il peut entrer un code différent que vous avez écrit il y a quelques semaines ou votre code actuel dans quelques mois.

Le symptôme est vraiment, vraiment bizarre. Vous pouvez imprimer la valeur de la variable à l'intérieur d'un composant jsx en utilisant la {x}syntaxe et plus tard imprimer la même variable en utilisant console.log après le rendu du composant jsx (pas avant) et trouver que la console.logvaleur est périmée - le console.logqui se produit après le rendu peut en quelque sorte avoir une valeur plus ancienne que le rendu.

Ainsi, la valeur réelle des variables d'état peut ne pas toujours fonctionner correctement dans le code normal - elles sont uniquement conçues pour renvoyer la dernière valeur d'un rendu. Pour cette raison, le mécanisme de rappel dans un setter d'état a été implémenté pour vous permettre d'obtenir la dernière valeur d'une variable d'état dans le code normal en dehors d'un rendu.