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

15voto

Geoman Points 87

J'ai écrit un hook personnalisé avec typescript si quelqu'un en a encore besoin.

import React, { useEffect, useRef, useState } from "react";

export const useStateWithCallback = <T>(initialState: T): [state: T, setState: (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => void] => {
    const [state, setState] = useState<T>(initialState);
    const callbackRef = useRef<(updated: T) => void>();

    const handleSetState = (updatedState: React.SetStateAction<T>, callback?: (updatedState: T) => void) => {
        callbackRef.current = callback;
        setState(updatedState);
    };

    useEffect(() => {
        if (typeof callbackRef.current === "function") {
            callbackRef.current(state);
            callbackRef.current = undefined;
        }
    }, [state]);

    return [state, handleSetState];
}

11voto

Laura Nutt Points 123

setState() met en file d'attente les changements d'état du composant et indique à React que ce composant et ses enfants doivent être rendus à nouveau avec l'état mis à jour.

La méthode setState est asynchrone, et de fait, elle ne renvoie pas de promesse. Donc, dans les cas où nous voulons mettre à jour ou appeler une fonction, la fonction peut être appelée callback dans la fonction setState comme deuxième argument. Par exemple, dans votre cas ci-dessus, vous avez appelé une fonction en tant que callback setState.

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

Le code ci-dessus fonctionne bien pour le composant de classe, mais dans le cas du composant fonctionnel, nous ne pouvons pas utiliser la méthode setState, et nous pouvons utiliser le hook use effect pour obtenir le même résultat.

La méthode évidente qui vient à l'esprit et que vous pouvez utiliser avec useEffect est la suivante :

const [state, setState] = useState({ name: "Michael" })

useEffect(() => {
  console.log(state) // do something after state has updated
}, [state])

Mais cela se produirait également lors du premier rendu, nous pouvons donc modifier le code comme suit : nous vérifions l'événement du premier rendu et évitons le rendu de l'état. Par conséquent, l'implémentation peut être faite de la manière suivante :

Nous pouvons utiliser le crochet utilisateur ici pour identifier le premier rendu.

Le crochet useRef nous permet de créer des variables mutables dans les composants fonctionnels. Il est utile pour accéder aux nœuds DOM/éléments React et pour stocker des variables mutables sans déclencher un re-rendu.

const [state, setState] = useState({ name: "Michael" });
const firstTimeRender = useRef(true);

useEffect(() => {
 if (!firstTimeRender.current) {
    console.log(state);
  }
}, [state])

useEffect(() => { 
  firstTimeRender.current = false 
}, [])

10voto

Kaiwen Luo Points 51

Vous pouvez utiliser les méthodes suivantes que je connais pour obtenir le dernier état après la mise à jour :

  1. useEffect
    https://reactjs.org/docs/hooks-reference.html#useeffect

    const [state, setState] = useState({name: "Michael"});
    
    const handleChangeName = () => {
      setState({name: "Jack"});
    }
    
    useEffect(() => {
      console.log(state.name); //"Jack"
    
      //do something here
    }, [state]);
  2. actualisation fonctionnelle
    https://reactjs.org/docs/hooks-reference.html#functional-updates
    " Si le nouvel état est calculé en utilisant l'état précédent, vous pouvez passer une fonction à setState. La fonction recevra la valeur précédente, et renverra une valeur mise à jour. "

    const [state, setState] = useState({name: "Michael"});
    
    const handleChangeName = () => {
      setState({name: "Jack"})
      setState(prevState => {
        console.log(prevState.name);//"Jack"
    
        //do something here
    
        // return updated state
        return prevState;
      });
    }
  3. useRef
    https://reactjs.org/docs/hooks-reference.html#useref
    "L'objet ref renvoyé persistera pendant toute la durée de vie du composant."

    const [state, setState] = useState({name: "Michael"});
    
    const stateRef = useRef(state);
    stateRef.current  = state;
    const handleClick = () => {
      setState({name: "Jack"});
    
      setTimeout(() => {
        //it refers to old state object
        console.log(state.name);// "Michael";
    
        //out of syntheticEvent and after batch update
        console.log(stateRef.current.name);//"Jack"
    
        //do something here
      }, 0);
    }

Dans le gestionnaire d'événement synthétique de react, setState est un processus de mise à jour par lots, de sorte que chaque changement d'état sera attendu et renverra un nouvel état.
"setState() ne met pas toujours immédiatement à jour le composant. Elle peut mettre en lot ou reporter la mise à jour à plus tard. ",
https://reactjs.org/docs/react-component.html#setstate

Voici un lien utile
Est-ce que React garde l'ordre pour les mises à jour d'état ?

3voto

sugaith Points 116

Grâce à votre aide à tous, j'ai pu réaliser ce crochet personnalisé :

Très similaire à la classe this.setState(state, callback)

const useStateWithCallback = (initialState) => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(() => undefined);

  const setStateCB = (newState, callback) => {
    callbackRef.current = callback;
    setState(newState);
  };

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

  return [state, setStateCB];
};

De cette façon, nous pouvons l'utiliser comme

const [isVisible, setIsVisible] = useStateWithCallback(false);

...

setIsVisible(true, () => console.log('callback called now!! =)');

Restez calme et bon codage !

2voto

Akash Singh Points 387

J'avais un cas d'utilisation où je voulais faire un appel api avec quelques paramètres après que l'état soit fixé. Je ne voulais pas définir ces paramètres comme mon état, j'ai donc créé un hook personnalisé et voici ma solution

import { useState, useCallback, useRef, useEffect } from 'react';
import _isFunction from 'lodash/isFunction';
import _noop from 'lodash/noop';

export const useStateWithCallback = initialState => {
  const [state, setState] = useState(initialState);
  const callbackRef = useRef(_noop);

  const handleStateChange = useCallback((updatedState, callback) => {
    setState(updatedState);
    if (_isFunction(callback)) callbackRef.current = callback;
  }, []);

  useEffect(() => {
    callbackRef.current();
    callbackRef.current = _noop; // to clear the callback after it is executed
  }, [state]);

  return [state, handleStateChange];
};

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