Uma observação sobre como o React atualiza o estado





Bom dia amigos!



O gancho useState () gerencia o estado nos componentes funcionais do React. Em beans de classe, o estado é armazenado em this.state e o método this.setState () é chamado para atualizar.



Normalmente, não há nada difícil em trabalhar com o estado. No entanto, há uma nuance importante associada à atualização.



Como o estado é atualizado: imediatamente (de forma síncrona) ou diferido (de forma assíncrona)? Continue lendo para descobrir a resposta.



1. Atualizando o estado com useState ()



Digamos que temos esse componente funcional:



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) define o estado inicial do componente. count é uma variável que contém o estado atual e setCount é uma função para atualizar esse estado.



O componente contém um botão de aumento duplo. Quando este botão é clicado, o manipulador doubleIncreaseHandler é chamado, realizando duas atualizações sucessivas para contar: setCount (contagem + 1) e então setCount (contagem + 1) novamente.



Qual será o estado do componente após clicar no botão 1 ou 2?



Abra esta demonstração e clique no botão. O valor da contagem aumentará em 1 após cada clique.



Quando setCount (count + 1) atualiza o estado, o valor de contagem não muda imediatamente. Em vez disso, o React agenda o estado a ser atualizado e, na próxima vez em que for renderizado, em const [count, setCount] = useState (0), o gancho atribui um novo valor para contar.



Por exemplo: se o valor da variável count for 0, chame setCount (count + 1); setCount (count + 1) avalia para setCount (0 + 1); setCount (0 + 1) - que resulta em 1 como o valor do estado na próxima renderização.



Portanto, a atualização do estado usando setValue (newValue) em [value, setValue] = useState () é feita de forma assíncrona.



No entanto, a função de atualização de estado pode receber um retorno de chamada como um argumento para calcular um novo estado com base no atual. No nosso caso, podemos usar 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>
    </>
  )
}

      
      





Ao atualizar o estado usando essa função, o argumento actualCount conterá o valor do estado real.



Abra esta demonstração e clique no botão. A contagem aumentará para 2 conforme o esperado.



Claro, sempre podemos criar uma variável intermediária:



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 é uma variável intermediária que pode ser atualizada como desejar. Esta variável é usada para atualizar o estado usando setCount (actualCount).



2. O estado é imutável (imutável) e somente leitura



Se você esquecer que o estado é atualizado na próxima renderização, você pode tentar ler o valor imediatamente após alterá-lo. Infelizmente, você não receberá nada:



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>
  )
}

      
      





O componente FetchUsers envia uma solicitação de montagem - startFetching ().



Quando os dados são recebidos, setUsers (fetchedUsers) atualiza o estado. No entanto, as mudanças não acontecem da noite para o dia.



A variável de usuários é imutável e somente leitura. Apenas o gancho useState () pode atribuir um novo valor a ele. Você não pode fazer isso diretamente:



  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. Atualizar o estado no componente de classe



As atualizações de estado assíncrono são comuns para componentes de classe.



Vamos considerar um exemplo:



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 });
  }
}

      
      





Observe o manipulador doubleIncrease (): ele usa uma função de retorno de chamada para atualizar o estado.



Abra esta demonstração e clique no botão. O valor de this.state aumentará para 2.



Em componentes de classe, this.state também não é atualizado instantaneamente. Quando this.setState (newState) é chamado, o React adia a atualização de this.state até a próxima renderização.



Portanto, this.setState (newState) atualiza this.state de forma assíncrona.



4. Conclusão



O gancho useState () e this.setState () (dentro do componente de classe) atualizam o valor da variável e o estado do componente de forma assíncrona.



Lembre-se de uma regra simples: chamar o setter setState (newValue) do gancho useState () (ou this.setState ()) não atualiza o estado imediatamente, mas na próxima renderização do componente.



Você percebeu que o React agora só precisa ser importado uma vez (em index.js)? Isso não é mais necessário nos componentes.



Obrigado pela atenção e tenha um bom dia.



All Articles