412 votes

Comment utiliser le callback `setState` sur les react hooks ?

React hooks introduit useState pour définir l'état des composants. Mais comment puis-je utiliser les hooks pour remplacer le callback comme le code ci-dessous :

setState(
  { name: "Michael" },
  () => console.log(this.state)
);

Je veux faire quelque chose après la mise à jour de l'état.

Je sais que je peux utiliser useEffect pour faire les choses supplémentaires mais je dois vérifier l'état de la valeur précédente ce qui nécessite un peu de code. Je cherche une solution simple qui peut être utilisée avec le logiciel useState crochet.

2 votes

Dans la classe component, j'ai utilisé async et await pour obtenir le même résultat que ce que vous avez fait pour ajouter un callback dans setState. Malheureusement, cela ne fonctionne pas dans le hook. Même si j'ai ajouté async et await, react n'attend pas la mise à jour de l'état. Peut-être que useEffect est le seul moyen de le faire.

2 votes

@Zhao, vous n'avez pas encore marqué la bonne réponse. Pouvez-vous gentiment nous accorder quelques secondes

430voto

Zohaib Ijaz Points 6945

Vous devez utiliser useEffect pour y parvenir.

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

const doSomething = () => {
  setCounter(123);
}

useEffect(() => {
   console.log('Do something after counter has changed', counter);
}, [counter]);

114 votes

Cela déclenchera le console.log sur le premier rendu ainsi qu'à tout moment counter changements. Que se passe-t-il si vous voulez faire quelque chose seulement après que l'état a été mis à jour, mais pas au moment du rendu initial, car la valeur initiale est définie ? Je suppose que vous pourriez vérifier la valeur dans useEffect et décider si vous voulez faire quelque chose à ce moment-là. Cela serait-il considéré comme une bonne pratique ?

3 votes

Pour éviter de courir useEffect lors du rendu initial, vous pouvez créer un personnalisé useEffect crochet qui ne s'exécute pas lors du rendu initial. Pour créer un tel crochet, vous pouvez consulter cette question : stackoverflow.com/questions/53253940/

76 votes

Et qu'en est-il du cas où je veux appeler différents callbacks dans différents appels setState, qui changeront la même valeur d'état ? Votre réponse est fausse, et ne devrait pas être marquée comme correcte pour ne pas embrouiller les débutants. La vérité est que les callbacks setState sont l'un des problèmes les plus difficiles lors de la migration vers des hooks depuis des classes qui n'ont pas de méthode de résolution claire. Parfois, vous aurez vraiment besoin d'un effet dépendant de la valeur, et parfois, vous aurez besoin d'une méthode compliquée, comme la sauvegarde d'une sorte de drapeau dans les références.

60voto

Bimal Grg Points 735

Si vous voulez mettre à jour l'état précédent, vous pouvez faire comme ceci dans les hooks :

const [count, setCount] = useState(0);

setCount(previousCount => previousCount + 1);

3 votes

Je pense qu'en setCounter vous voulez dire setCount

1 votes

@BimalGrg Est-ce que cela fonctionne vraiment ? Je ne peux pas le reproduire. Il ne parvient pas à compiler avec cette erreur : expected as assignment or function call and instead saw an expression

0 votes

@tonitone120 il y a une fonction flèche dans setCount.

54voto

ford04 Points 112

Mimic setState avec useEffect , uniquement sur l'état mises à jour (pas l'état initial) :

const [state, setState] = useState({ name: "Michael" })
const isFirstRender = useRef(true)
useEffect(() => {
  if (isFirstRender.current) {
    isFirstRender.current = false // toggle flag after first render/mounting
    return;
  }
  console.log(state) // do something after state has updated
}, [state])

Crochet personnalisé useEffectUpdate

function useEffectUpdate(callback) {
  const isFirstRender = useRef(true);
  useEffect(() => {
    if (isFirstRender.current) {
      isFirstRender.current = false; // toggle flag after first render/mounting
      return;
    }
    callback(); // performing action after state has updated
  }, [callback]);
}

// client usage, given some state dep
const cb = useCallback(() => { console.log(state) }, [state]); // memoize callback
useEffectUpdate(cb);

2 votes

Alternatif : useState peut être mis en œuvre pour recevoir un callback comme setState dans les classes.

47voto

MJ Studio Points 146

Je pense, en utilisant useEffect n'est pas un moyen intuitif.

J'ai créé un wrapper pour cela. Dans ce hook personnalisé, vous pouvez transmettre votre callback à setState au lieu du paramètre useState paramètre.

Je viens de créer la version Typescript. Donc si vous avez besoin de l'utiliser en Javascript, il suffit d'enlever certaines notations de type du code.

Utilisation

const [state, setState] = useStateCallback(1);
setState(2, (n) => {
  console.log(n) // 2
});

Déclaration

import { SetStateAction, useCallback, useEffect, useRef, useState } from 'react';

type Callback<T> = (value?: T) => void;
type DispatchWithCallback<T> = (value: T, callback?: Callback<T>) => void;

function useStateCallback<T>(initialState: T | (() => T)): [T, DispatchWithCallback<SetStateAction<T>>] {
  const [state, _setState] = useState(initialState);

  const callbackRef = useRef<Callback<T>>();
  const isFirstCallbackCall = useRef<boolean>(true);

  const setState = useCallback((setStateAction: SetStateAction<T>, callback?: Callback<T>): void => {
    callbackRef.current = callback;
    _setState(setStateAction);
  }, []);

  useEffect(() => {
    if (isFirstCallbackCall.current) {
      isFirstCallbackCall.current = false;
      return;
    }
    callbackRef.current?.(state);
  }, [state]);

  return [state, setState];
}

export default useStateCallback;

Drawback

Si la fonction flèche passée fait référence à une fonction externe variable, alors elle capturera la valeur actuelle et non une valeur après la mise à jour de l'état. Dans l'exemple d'utilisation ci-dessus, console.log(état) imprimera 1 et non 2.

20voto

Hugo Merzisen Points 209

Je rencontrais le même problème, l'utilisation de useEffect dans ma configuration n'a pas fonctionné (je mets à jour l'état d'un parent à partir d'un tableau de plusieurs composants enfants et j'ai besoin de savoir quel composant a mis à jour les données).

Envelopper setState dans une promesse permet de déclencher une action arbitraire après l'achèvement :

import React, {useState} from 'react'

function App() {
  const [count, setCount] = useState(0)

  function handleClick(){
    Promise.resolve()
      .then(() => { setCount(count => count+1)})
      .then(() => console.log(count))
  }

  return (
    <button onClick= {handleClick}> Increase counter </button>
  )
}

export default App;

La question suivante m'a mis sur la bonne voie : React met-il en lots les fonctions de mise à jour de l'état lorsqu'il utilise des crochets ?

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