Les hooks personnalisés useGetState résolvent le problème selon lequel le rappel asynchrone useState ne peut pas obtenir la dernière valeur

Deux façons de transmettre des paramètres à setState

1. Transmettez directement la nouvelle valeur setState(options);

const [state, setState] = useState(0);
setState(state + 1);

2. Transmettez la fonction de rappel setState(callBack);

const [state, setState] = useState(0);
setState((prevState) => prevState + 1); // prevState 是改变之前的 state 值,return 返回的值会作为新状态覆盖 state 值

Le rappel asynchrone useState ne peut pas obtenir la dernière valeur et la dernière solution

Dans des circonstances normales, setState peut utiliser directement la première méthode ci-dessus pour transmettre des paramètres, mais dans certains cas particuliers, la première méthode provoquera des exceptions ; par exemple, si vous souhaitez obtenir le dernier statut et définir le statut dans un rappel ou une fermeture asynchrone. , puis le premier Le statut obtenu de cette manière n'est pas en temps réel. La documentation officielle de React mentionne : Toute fonction à l'intérieur du composant, y compris les fonctions de gestion d'événements et l'effet, est "vue" depuis le rendu dans lequel elle a été créée, donc le référencé value Il est encore ancien et provoque finalement une exception dans setState :

import React, { useState, useEffect } from 'react';

const App = () => {
  const [arr, setArr] = useState([0]);

  useEffect(() => {
    console.log(arr);
  }, [arr]);

  const handleClick = () => {
    Promise.resolve().then(() => {
      setArr([...arr, 1]); // 此时赋值前 arr 为:[0]
    })
      .then(() => {
        setArr([...arr, 2]); // 此时赋值前 arr 为旧状态仍然为:[0]
      });
  }

  return (
    <>
      <button onClick={handleClick}>change</button>
    </>
  );
}

export default App;

// 输出结果
[0]
[0,1]
[0,2]

Dans le code ci-dessus, le composant App est en fait une fonction de fermeture. handleClick fait référence à arr. La valeur de arr est en effet mise à jour après le premier setArr. Nous pouvons également voir dans le résultat de sortie ci-dessus, mais la fonction de traitement d'événements handleClick exécutée cette fois La portée est toujours ancienne et l'arr référencé est toujours ancien, ce qui fait que le résultat après le deuxième setArr est [0, 2] :

Dans le composant de classe, nous pouvons utiliser setState(options, callBack) ; setState est à nouveau exécuté dans la fonction de rappel du deuxième paramètre de setState. Il n'y a pas de problème de portée de fermeture, mais useState dans React Hook supprime le deuxième paramètre de setState. Paramètres, et ce n'est pas bien s'il y a trop de nidifications ;

Solution 1

const handleClick = () => {
    Promise.resolve().then(() => {
      setArr(prevState => [...prevState, 1]); // 这里也可以不改,使用第一中传参方式 setArr([...arr, 1]); 因为这里不需要获取最新状态
    })
      .then(() => {
        setArr(prevState => [...prevState, 2]); // 这里必须改成回调函数传参方式,否则会读取旧状态,导致异常
      });
  }


// 输出结果
[0]
[0,1]
[0,1,2]

Nous avons constaté que lors de la transmission de paramètres en mode rappel, le résultat de sortie est correct.

Solution 2 :

Utilisez useReducer pour imiter forceUpdate dans les composants de classe afin d'implémenter le rendu forcé des composants ; Remarque : Cette solution n'est applicable que lorsque seule la page dépend des données. S'il existe des hooks comme useEffect qui ne peuvent pas capturer les modifications en temps réel lors de la surveillance des données ( arr dans l'exemple)

import React, { useState, useReducer } from 'react';

const App = () => {
  const [arr, setArr] = useState([0]);
  const [, forceUpdate] = useReducer(x => x + 1, 0);

  const handleClick = () => {
    Promise.resolve().then(() => {
      arr.push(1); // 如果这里也需要做一次渲染在改变状态后调用 forceUpdate() 即可
    })
      .then(() => {
        arr.push(2);
        forceUpdate();
      });
  }

  return (
    <>
      <h1>{arr.toString()}</h1>
      <button onClick={handleClick}>change</button>
    </>
  );
}

export default App;

Solution 3 :

En utilisant ref, l'état change et la valeur est mappée à ref. Le changement de ref ne déclenchera pas de mise à jour de page, mais la dernière valeur peut être obtenue de manière asynchrone, donc si elle doit être utilisée sur la page, utilisez state, et si il est utilisé en logique asynchrone, utilisez ref

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

const App = () => {
  const [arr, setArr] = useState([0]);
  let ref = useRef();
  useEffect(() => {
    ref.current = arr;
    console.log(arr);
  }, [arr]);

  const handleClick = () => {
    Promise.resolve().then(() => {
      const now = [...ref.current, 1];
      ref.current = now;
      setArr(now);
    })
      .then(() => {
        setArr([...ref.current, 2]);
      });
  }

  return (
    <>
      <h1>{arr.toString()}</h1>
      <button onClick={handleClick}>change</button>
    </>
  );
}

export default App;

Option 4, option recommandée :

Des méthodes telles que l'exemple 3 ci-dessus peuvent encapsuler un hook pour associer l'état et la référence, et en même temps fournir une méthode pour obtenir la dernière valeur de manière asynchrone, par exemple :

const useGetState = (initVal) => {
  const [state, setState] = useState(initVal);
  const ref = useRef(initVal);
  const setStateCopy = (newVal) => {
    ref.current = newVal;
    setState(newVal);
  }
  const getState = () => ref.current;
  return [state, setStateCopy, getState];
}

const App = () => {
  const [arr, setArr, getArr] = useGetState([0]);
  useEffect(() => {
    console.log(arr);
  }, [arr]);

  const handleClick = () => {
    Promise.resolve().then(() => {
      setArr([...getArr(), 1]);
    })
      .then(() => {
        setArr([...getArr(), 2]);
      });
  }

  return (
    <>
      <h1>{arr.toString()}</h1>
      <button onClick={handleClick}>change</button>
    </>
  );
}

Lorsque le rappel asynchrone useState ne peut pas obtenir la dernière valeur, nous recommandons la solution 1 et la solution 4. La solution 1 est plus simple et la solution 4 est plus parfaite. Les deux sont recommandées, en fonction de votre scénario d'utilisation.

Je suppose que tu aimes

Origine blog.csdn.net/weixin_44786530/article/details/132792343
conseillé
Classement