Este artigo explica os Ganchos React useState
e useRef
. Você aprenderá seu uso básico e conhecerá os diferentes casos de uso para os dois ganchos.
Você pode encontrar os exemplos como parte de um CodeSandbox . Para ver os diferentes exemplos em ação, basta adaptar a seguinte linha em App.js:
exportar AppDemo6 padrão;//mude para AppDemo
Compreendendo o useState
Hook
O gancho useState
permite o desenvolvimento do estado do componente para componentes funcionais. Antes do React 16.8, o estado local para um componente só era possível com componentes baseados em classe.
Dê uma olhada no código a seguir.
import {useState} de"react"; function AppDemo1 () { const stateWithUpdater=useState (true); const darkMode=stateWithUpdater [0]; const darkModeUpdater=stateWithUpdater [1]; Retorna (); }{darkMode?"modo escuro ativado":"modo escuro desativado"}
O gancho useState
retorna uma matriz com dois itens. No exemplo, implementamos um estado de componente booleano e inicializamos nosso Hook com true
.
Este único argumento de useState
é considerado apenas durante o ciclo de renderização inicial. Se precisar de um valor inicial complexo de calcular, no entanto, você pode passar uma função de retorno de chamada para fins de otimização de desempenho.
O primeiro item da matriz representa o estado real e o segundo item constitui a função de atualização de estado. O manipulador onClick
demonstra como usar a função de atualização ( darkModeUpdate
) para alterar a variável de estado ( darkMode
). É importante atualizar seu estado exatamente assim. O código a seguir é ilegal:
darkMode=true;
Se você tem alguma experiência com o gancho useState
, pode estar se perguntando sobre a sintaxe do meu exemplo. O uso padrão é utilizar os itens de array retornados com a ajuda de desestruturação de array .
const [darkMode, setDarkMode]=useState (true);
Como um lembrete, é crucial seguir as regras dos Ganchos ao usar qualquer Gancho, não apenas useRef
:
- Os ganchos só devem ser chamados a partir do nível superior de sua função React
- Os ganchos não devem ser chamados de código aninhado (por exemplo, loops, condições)
- Ganchos também podem ser chamados no nível superior de Ganchos personalizados
Agora que cobrimos o básico, vamos examinar todos os aspectos do Gancho com o código de exemplo a seguir.
import {useState} de"react"; import"./styles.css"; function AppDemo2 () { console.log ("renderizar aplicativo"); const [darkMode, setDarkMode]=useState (false); Retorna (); }O gancho useState
Clique no botão para alternar o estado
{ setDarkMode (! darkMode); }} > alternar modo escuro
Se darkMode
for definido como true
, uma classe CSS adicional ( dark-mode
) será adicionada a className
, e as cores do fundo e do texto são invertidas. Como você pode ver na saída do console na gravação, toda vez que o estado muda, o componente correspondente é renderizado novamente.
App
. React DevTools é especialmente útil aqui para destacar as atualizações visualmente quando os componentes são renderizados. Na última gravação, você pode ver a borda piscando ao redor do componente que o notifica sobre outro ciclo de renderização do componente.
No próximo exemplo, os cabeçalhos são extraídos em um componente React separado ( Descrição
).
import {useState} de"react"; import"./styles.css"; function AppDemo3 () { console.log ("renderizar aplicativo"); const [darkMode, setDarkMode]=useState (false); Retorna (); } Descrição const=()=> { console.log ("render Descrição"); Retorna ( <>{ setDarkMode (! darkMode); }} > alternar modo escuro O gancho useState
Clique no botão para alternar o estado
> ); };
O componente App
é renderizado sempre que o usuário clica no botão porque o manipulador de clique correspondente atualiza a variável de estado darkMode
. Além disso, o componente filho Description
também é renderizado.
App
e os componentes filhos. O diagrama a seguir ilustra que uma mudança de estado causa um ciclo de renderização.
Por que é importante entender o ciclo de vida do React Hooks? Por outro lado, o estado é preservado sobre a renderização, desde que você não atualize o estado por meio da função atualizador, que por si só aciona um ciclo de renderização renovado.
Usando o gancho useState
com useEffect
Outro conceito importante a ser entendido é o gancho useEffect
, que você provavelmente tem que usar em seu aplicativo para invocar código assíncrono (por exemplo, para buscar dados). Como você pode ver no diagrama anterior, os ganchos useState
e useEffect
são fortemente acoplados porque as mudanças de estado podem invocar efeitos.
Vejamos o exemplo a seguir. Introduzimos duas variáveis de estado adicionais: carregando
e lang
. O efeito é invocado sempre que o prop url
muda. Ele busca uma string de idioma ( en
ou de
) e atualiza o estado com a função de atualização setLang
.
Dependendo do idioma, uma string em inglês ou alemão dentro do título é renderizada. Além disso, durante o processo de busca, um estado carregando
é definido e, dependendo do valor ( true
ou false
), um indicador de carregamento é renderizado em vez do título.
import {useEffect, useState} de"react"; import"./styles.css"; function App4 ({url}) { console.log ("renderizar aplicativo"); const [carregando, setLoading]=useState (true); const [lang, setLang]=useState ("de"); const [darkMode, setDarkMode]=useState (false); useEffect (()=> { console.log ("useEffect"); const fetchData=função assíncrona () { tentar { setLoading (true); resposta const=espera axios.get (url); if (response.status===200) { const {idioma}=resposta.data; setLang (idioma); } } catch (erro) { erro de lançamento; } finalmente { setLoading (false); } }; fetchData (); }, [url]); Retorna ({carregando ? (); }Carregando...): ( <>{lang==="en" ?"O gancho useState é incrível" :"Der useState Hook ist toll"}
{ setDarkMode (! darkMode); }} > alternar modo escuro > )}
useEffect
. Suponhamos que queremos alternar para o modo escuro sempre que buscarmos o idioma atual. Adicionamos uma chamada ao atualizador setDarkMode
depois de atualizar o idioma. Além disso, precisamos adicionar o estado darkMode
como uma dependência ao array de dependência do efeito.
Por que isso deve ser feito vai além do escopo deste artigo, mas você pode ler sobre o
useEffect
Hook em grande detalhe em meu post anterior.
import {useEffect, useState} de"react"; import"./styles.css"; function AppDemo5 ({url}) { console.log ("renderizar aplicativo"); const [carregando, setLoading]=useState (true); const [lang, setLang]=useState ("de"); const [darkMode, setDarkMode]=useState (false); useEffect (()=> { console.log ("useEffect"); const fetchData=função assíncrona () { tentar { setLoading (true); resposta const=espera axios.get (url); if (response.status===200) { const {idioma}=resposta.data; setLang (idioma); setDarkMode (! darkMode); } } catch (erro) { erro de lançamento; } finalmente { setLoading (false); } }; fetchData (); }, [url, darkMode]); Retorna ({carregando ? (); }Carregando...): ( <>{lang==="en" ?"O gancho useState é incrível" :"Der useState Hook ist toll"}
{ setDarkMode (! darkMode); }} > alternar modo escuro > )}
Infelizmente, causamos um loop infinito.
useEffect
causa um loop infinito. Por que isso? Como adicionamos darkMode
à matriz de dependência do efeito e atualizamos esse estado exato dentro do efeito, o efeito é invocado novamente, atualiza o estado novamente e isso continua indefinidamente.
Mas há uma saída! Podemos evitar o darkMode
como a dependência do efeito calculando o novo estado a partir do anterior estado . Chamamos o atualizador setDarkMode
de maneira diferente, passando uma função que tem o estado anterior como argumento.
A implementação revisada de useEffect
tem a seguinte aparência:
useEffect (()=> { console.log ("useEffect"); const fetchData=função assíncrona () { tentar { setLoading (true); resposta const=espera axios.get (url); if (response.status===200) { const {idioma}=resposta.data; setLang (idioma); setDarkMode ((anterior)=>! anterior);//sem acesso ao estado darkMode } } catch (erro) { erro de lançamento; } finalmente { setLoading (false); } }; fetchData (); }, [url]);//sem dependência darkMode
Diferenças de componentes baseados em classe
Se você usa o React há muito tempo ou atualmente está trabalhando em um código legado, conhece os componentes baseados em classes. Com componentes baseados em classe, você tem um único objeto que representa o estado do componente. Para atualizar uma fatia do estado geral, você pode aproveitar o [setState] genérico ( https://reactjs.org/docs/state-and-lifecycle.html )
método.
Imagine que queremos apenas atualizar a variável de estado darkMode
. Em seguida, você pode simplesmente colocar a propriedade atualizada no objeto; o resto do estado permanece inalterado.
this.setState ({darkMode: false});
Com componentes funcionais, no entanto, a maneira preferida é usar variáveis de estado atômicas que podem ser atualizadas individualmente. Caso contrário, você pode rapidamente se encontrar no vale das lágrimas.
Comparado com AppDemo6
, o seguinte componente ( AppDemo7
) foi refatorado apenas em relação ao gerenciamento de estado. Em vez de três variáveis de estado atômicas com tipos de dados primitivos, usamos um objeto de estado ( estado
).
import {useEffect, useState} de"react"; import"./styles.css"; function AppDemo7 ({url}) { const initialState={ carregando: verdadeiro, lang:"de", darkMode: true }; const [estado, setState]=useState (initialState); console.log ("renderizar aplicativo", estado); useEffect (()=> { console.log ("useEffect"); const fetchData=função assíncrona () { tentar { setState ((anterior)=> ({ carregando: verdadeiro, lang: prev.lang, darkMode: prev.darkMode })); resposta const=espera axios.get (url); if (response.status===200) { const {idioma}=resposta.data; setState ((anterior)=> ({ lang: idioma, darkMode:! prev.darkMode, carregando: prev.loading })); } } catch (erro) { erro de lançamento; } finalmente { setState ((anterior)=> ({ carregando: falso, lang: prev.lang, darkMode: prev.darkMode })); } }; fetchData (); }, [url]); Retorna ({state.loading? (); }Carregando...): ( <>{state.lang==="en" ?"O gancho useState é incrível" :"Der useState Hook ist toll"}
{ setState ((anterior)=> ({ darkMode:! prev.darkMode, //lang: prev.lang, carregando: prev.loading })); }} > alternar modo escuro > )}
Como você pode ver, o código é confuso e difícil de manter. Também inclui um bug ilustrado com uma propriedade comentada no manipulador onClick
. Quando o usuário clica no botão, o estado geral não é calculado corretamente.
Nesse caso, a propriedade lang
não está presente. Isso leva a um bug que faz com que o texto seja renderizado em alemão, já que state.lang
é undefined
. Espero ter mostrado definitivamente que esta é uma má ideia. A propósito, a equipe React não recomenda isso qualquer um .
Compreendendo o useRef
Hook
O gancho useRef
é semelhante ao useState , mas diferente
. Antes de esclarecer isso, explicarei seu uso básico.
import {useRef} de'react'; const AppDemo8=()=> { const ref1=useRef (); const ref2=useRef (2021); console.log ("render"); console.log (ref1, ref2); Retorna (); };{ref1.current}
{ref2.current}
O resultado não é espetacular, mas mostra o ponto crucial.
propriedade
atual. Inicializamos duas referências (também conhecidas como refs) chamando. A chamada de Hook retorna um objeto que possui uma propriedade current
, que armazena o valor real. Se você passar um argumento initialValue
para useRef (initialValue)
, esse valor será armazenado em current
.
Essa é a razão pela qual a primeira saída console.log
armazena undefined
: porque invocamos o Gancho sem nenhum argumento. Não se preocupe, podemos atribuir valores mais tarde.
Para acessar o valor de um ref, você precisa acessar sua propriedade current
, como fizemos na parte JSX. refs estão diretamente disponíveis na renderização inicial logo após terem sido definidos.
Mas por que precisamos de useRef
? Por que não usar variáveis let
comuns em vez disso ? Controle-se-voltaremos a isso.
Casos de uso comuns para useRef
Vamos dar uma olhada no exemplo a seguir.
import {useRef} de"react"; import"./styles.css"; const AppDemo9=()=> { const countRef=useRef (0); console.log ("render"); Retorna (); };contagem: {countRef.current}
{ countRef.current=countRef.current + 1; console.log (countRef.current); }} > aumentar a contagem
Nosso objetivo é definir um ref chamado countRef
, inicializar o valor com 0
e aumentar esta variável de contador a cada clique de botão. O valor da contagem renderizada deve ser atualizado. Infelizmente, isso não funciona-até mesmo a saída do console prova que a propriedade current
contém as atualizações corretas.
Como você pode ver na renderização de outra saída do console, nosso componente não é renderizada novamente. Poderíamos utilizar useState
em vez de ter esse comportamento.
O quê? Portanto, useRef
é bastante inútil? Não tão rápido-é útil em combinação com outros Ganchos que acionam reprocessadores, como useState
, useReducer
e useContext
.
Você deve pensar em useRef
como outra ferramenta em sua caixa de ferramentas e deve saber quando usá-la. Lembra do diagrama do ciclo de vida do componente visto acima? Os valores de refs persistem (especificamente a propriedade current
) ao longo dos ciclos de renderização. Não é um bug; é um recurso.
Considere situações em que você deseja atualizar os dados de um componente (ou seja, suas variáveis de estado) para acionar uma renderização a fim de atualizar a IU. Você também pode ter situações em que deseja o mesmo comportamento com uma exceção: você não deseja acionar um ciclo de renderização porque isso pode levar a bugs, experiência do usuário estranha (por exemplo, oscilações) ou problemas de desempenho.
Você pode pensar em refs como variáveis de instância de componentes baseados em classe Um ref é um contêiner genérico para armazenar qualquer tipo de dados, como dados primitivos ou objetos.
Tudo bem, mostraremos um exemplo útil.
import {useState} de"react"; import"./styles.css"; const AppDemo10=()=> { const [valor, setValue]=useState (""); console.log ("render"); const handleInputChange=(e)=> { setValue (e.target.value); }; Retorna (); };
Como você pode ver na gravação abaixo, este componente apenas renderiza um campo de entrada e armazena seu valor na variável de estado valor
. A saída do console revela que o componente AppDemo10
é renderizado novamente a cada pressionamento de tecla.
Esse pode ser o comportamento que você deseja, por exemplo, realizar uma operação como uma pesquisa em cada caractere. Isso é chamado de componente controlado . No entanto, pode ser exatamente o oposto e as renderizações tornam-se problemáticas. Então você precisa de um componente não controlado .
Vamos reescrever o exemplo para usar um componente não controlado com useRef
. Como consequência, precisamos de um botão para atualizar o estado do componente e armazenar o campo de entrada totalmente preenchido.
import {useState, useRef} de"react"; import"./styles.css"; const AppDemo11=()=> { const [valor, setValue]=useState (""); const valueRef=useRef (); console.log ("render"); const handleClick=()=> { console.log (valorRef); setValue (valueRef.current.value); }; Retorna (); };Valor: {value}
Com esta solução, não causamos ciclos de renderização a cada pressionamento de tecla. Por outro lado, precisamos “enviar” a entrada com um botão para atualizar a variável de estado valor
. As you can see from the console output, the second render first occurs on button click.
By the way, the example above shows the second use case for refs.
With the ref
property, React provides direct access to React components or HTML elements. The console output reveals that we indeed have access to the input
element. The reference is stored in the current
property.
This constitutes the second use case of useRef
besides utilizing it as a generic container persisting data throughout the component lifecycle. If you need direct access to a DOM element, you can leverage the ref
prop. The next example shows how to focus the input field after the component was initialized.
import { useEffect, useRef } from"react"; import"./styles.css"; const AppDemo12=()=> { const inputRef=useRef(); console.log("render"); useEffect(()=> { console.log("useEffect"); inputRef.current.focus(); }, []); return (); };
Inside the useEffect
callback, we call the native focus
method.
This technique is also widely used in React projects in combination with third-party (non-React) components when you need direct access to DOM elements.
Another common use case is when you need the state value of the previous render cycle. The following example shows how to do this. Of course, you could also extract the logic into a custom usePrevious
Hook.
import { useEffect, useState, useRef } from"react"; import"./styles.css"; const AppDemo13=()=> { console.log("render"); const [count, setCount]=useState(0); //Get the previous value (was passed into hook on last render) const ref=useRef(); //Store current value in ref useEffect(()=> { console.log("useEffect"); ref.current=count; }, [count]);//Only re-run if value changes return (); };Now: {count}, before: {ref.current}
After the initial render, an effect is executed that assigns the state variable count
to the ref.current
. Because no additional render occurs, the rendered value is undefined
. A click on the button triggers a state update because of a call to setCount
.
Next, the UI gets re-rendered, and the before label shows the correct value (0
). After rendering, another effect is invoked. Now 1
gets assigned to our ref, and so on.
"data-medium-file="https://blog.logrocket.com/wp-content/uploads/2021/05/access-previous-state-useref-300x183.gif"data-large-file="https://br.atsit.in/wp-content/uploads/2021/05/usestate-vs-useref-semelhancas-diferencas-e-casos-de-uso-5.gif"loading="lazy"class="wp-image-50116 size-full"src="https://br.atsit.in/wp-content/uploads/2021/05/usestate-vs-useref-semelhancas-diferencas-e-casos-de-uso-5.gif"alt="Accessing Previous State Via useRef"width="612"height="374"/>useRef
.
It’s important to note that all refs need to get updated either inside a useEffect
callback or inside handlers. Mutating the ref during rendering, i.e., from places other than those just mentioned, might introduce bugs. The same applies to useState
, too.
Why let
can’t replace useRef
Now I still owe you a resolution for why a let
variable does not replace the concept of a ref. The next example replaces the use of useRef
with a vanilla JavaScript variable assignment from inside the useEffect
Hook.
import { useEffect, useState } from"react"; import"./styles.css"; const AppDemo14=()=> { console.log("render"); const [count, setCount]=useState(0); let prevCount; useEffect(()=> { console.log("useEffect", prevCount); prevCount=count; }, [count]); return (); };Now: {count}, before: {prevCount}
However, the following recording will reveal that this does not work. The console output reinforces the problem because the assignment inside of useEffect
gets overridden on every new render cycle. undefined
is implicitly assigned because of let prevCount;
.
Even the mighty ESLint Rules of Hooks plugin tells you that we should utilize useRef
instead.
The differences between useRef
and useState
at a glance
The following differences have already been discussed in detail but are presented here again in a succinctly summarized form:
- Both preserve their data during render cycles and UI updates, but only the
useState
Hook with its updater function causes re-renders useRef
returns an object with acurrent
property holding the actual value. In contrast,useState
returns an array with two elements: the first item constitutes the state, and the second item represents the state updater functionuseRef
‘scurrent
property is mutable, butuseState
‘s state variable not. In contrast to thecurrent
property ofuseRef
, you should not directly assign values to the state variable ofuseState
. Instead, always use the updater function (i.e., the second array item). As the React team recommends in the documentation forsetState
in class-based components (but still true for function components), treat state like an immutable variableuseState
anduseRef
can be considered data Hooks, but onlyuseRef
can be used in yet another field of application: to gain direct access to React components or DOM elements
Conclusão
This article addresses the useState
and useRef
Hooks. It should be clear at this point that there is no such thing as a good or a bad Hook. You need both Hooks for your React applications because they are designed for different applications.
If you want to update data and cause a UI update, useState
is your Hook. If you need some kind of data container throughout the component’s lifecycle without causing render cycles on mutating your variable, then useRef
is your solution.
The post useState
vs. useRef
: Similarities, differences, and use cases appeared first on LogRocket Blog.