Une note sur la façon dont React met à jour l'état





Bonne journée, mes amis!



Le hook useState () gère l'état dans les composants fonctionnels React. Dans les beans de classe, l'état est stocké dans this.state et la méthode this.setState () est appelée pour mettre à jour.



En général, il n'y a rien de difficile à travailler avec l'État. Cependant, il existe une nuance importante associée à sa mise à jour.



Comment l'état est-il mis à jour: immédiatement (de manière synchrone) ou différé (de manière asynchrone)? Lisez la suite pour trouver la réponse.



1. Mise à jour de l'état avec useState ()



Disons que nous avons un tel composant fonctionnel:



import { useState } from 'react'

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

  const doubleIncreaseHandler = () => {
    setCount(count + 1)
    setCount(count + 1)
  }

  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





const [count, setCount] = useState (0) définit l'état initial du composant. count est une variable contenant l'état actuel et setCount est une fonction pour mettre à jour cet état.



Le composant contient un bouton Double Augmentation. Lorsque vous cliquez sur ce bouton, le gestionnaire doubleIncreaseHandler est appelé, qui effectue deux mises à jour successives pour count: setCount (count + 1) puis setCount (count + 1) à nouveau.



Quel sera l'état du composant après avoir cliqué sur le bouton 1 ou 2?



Ouvrez cette démo et cliquez sur le bouton. La valeur de comptage augmentera de 1 après chaque clic.



Lorsque setCount (count + 1) met à jour l'état, la valeur de comptage ne change pas immédiatement. Au lieu de cela, React planifie l'état à mettre à jour et au prochain rendu, dans const [count, setCount] = useState (0), le hook affecte une nouvelle valeur à count.



Par exemple: si la valeur de la variable count est 0, alors appelez setCount (count + 1); setCount (count + 1) donne la valeur setCount (0 + 1); setCount (0 + 1) - ce qui donne 1 comme valeur d'état lors du prochain rendu.



Par conséquent, la mise à jour de l'état à l'aide de setValue (newValue) dans [value, setValue] = useState () est effectuée de manière asynchrone.



Cependant, la fonction de mise à jour d'état peut prendre un rappel comme argument pour calculer un nouvel état basé sur l'actuel. Dans notre cas, nous pouvons utiliser setCount (actualCount => actualCount + 1):



import { useState } from 'react'

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

  const doubleIncreaseHandler = () => {
    setCount(actualCount => actualCount + 1)
    setCount(actualCount => actualCount + 1)
  }

  return (
    <>
      <button onClick={doubleIncreaseHandler}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





Lors de la mise à jour de l'état à l'aide de cette fonction, l'argument actualCount contiendra la valeur réelle de l'état.



Ouvrez cette démo et cliquez sur le bouton. Le nombre augmentera à 2 comme prévu.



Bien entendu, on peut toujours créer une variable intermédiaire:



import { useState } from 'react'

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

  const doubleIncrease = () => {
    let actualCount = count
    actualCount = actualCount + 1
    actualCount = actualCount + 1
    setCount(actualCount)
  }

  return (
    <>
      <button onClick={this.doubleIncrease}>
        Double Increase
      </button>
      <div>Count: {count}</div>
    </>
  )
}

      
      





let actualCount = count est une variable intermédiaire qui peut être mise à jour à votre guise. Cette variable est utilisée pour mettre à jour l'état à l'aide de setCount (actualCount).



2. L'état est immuable (immuable) et en lecture seule



Si vous oubliez que l'état est mis à jour lors du prochain rendu, vous pouvez essayer de lire la valeur immédiatement après sa modification. Malheureusement, vous n'obtiendrez rien:



function FetchUsers() {
  const [users, setUsers] = useState([])

  useEffect(() => {
    const startFetching = async () => {
      const response = await fetch('/users')
      const fetchedUsers = await response.json()

      setUsers(fetchedUsers)
      console.log(users)        // => []
      console.log(fetchedUsers) // => ['John', 'Jane', 'Alice', 'Bob']
    }
    startFetching()
  }, [])

  return (
    <ul>
      {users.map(user => <li>{user}</li>)}
    </ul>
  )
}

      
      





Le composant FetchUsers envoie une demande de montage - startFetching ().



Lorsque les données sont reçues, setUsers (fetchedUsers) met à jour l'état. Cependant, les changements ne se produisent pas du jour au lendemain.



La variable users est immuable et en lecture seule. Seul le hook useState () peut lui attribuer une nouvelle valeur. Vous ne pouvez pas faire cela directement:



  function FetchUsers() {
    const [users, setUsers] = useState([])

    useEffect(() => {
      const startFetching = async () => {
        const response = await fetch('/users')
        const fetchedUsers = await response.json()

        users = fetchedUsers       // ! users    
        users.push(...fetchedUsers) // ! users 
        setUsers(fetchedUsers)     // !
      }
      startFetching()
    }, [])

    return (
      <ul>
        {users.map(user => <li>{user}</li>)}
      </ul>
    )
  }

      
      





3. Mise à jour de l'état dans le composant de classe



Les mises à jour d'état asynchrones sont courantes pour les composants de classe.



Prenons un exemple:



import { Component } from 'react';

class DoubleIncreaser extends Component {
  state = {
    count: 0
  };

  render() {
    return (
      <>
        <button onClick={this.doubleIncrease}>
          Double Increase
        </button>
        <div>Count: {this.state.count}</div>
      </>
    );
  }

  doubleIncrease = () => {
    // !
    this.setState(({ count }) => ({
      count: count + 1
    }));
    this.setState(({ count }) => ({
      count: count + 1
    }));

    //  !
    // this.setState({ count: this.state.count + 1 });
    // this.setState({ count: this.state.count + 1 });
  }
}

      
      





Remarquez le gestionnaire doubleIncrease (): il utilise une fonction de rappel pour mettre à jour l'état.



Ouvrez cette démo et cliquez sur le bouton. La valeur de this.state passera à 2.



Dans les composants de classe, this.state n'est pas non plus mis à jour instantanément. Lorsque this.setState (newState) est appelé, React diffère la mise à jour de this.state jusqu'au prochain rendu.



Donc this.setState (newState) met à jour this.state de manière asynchrone.



4. Conclusion



Le hook useState () et this.setState () (à l'intérieur du composant de classe) mettent à jour la valeur de la variable et l'état du composant de manière asynchrone.



Souvenez-vous d'une règle simple: l'appel du setState (newValue) setter du hook useState () (ou this.setState ()) ne met pas à jour l'état immédiatement, mais au prochain rendu du composant.



Avez-vous remarqué que React ne doit maintenant être importé qu'une seule fois (dans index.js)? Vous n'avez plus besoin de le faire dans les composants.



Merci pour votre attention et bonne journée.



All Articles