Até agora nossos componentes, sendo funções Rust, não tinham estado – eles estavam sempre renderizando a mesma coisa. No entanto, em um componente de interface do usuário, geralmente é útil ter uma funcionalidade com estado para criar interações do usuário. Por exemplo, você pode querer rastrear se o usuário abriu uma lista suspensa e renderizar coisas diferentes de acordo.
Para lógica com estado, você pode usar hooks. Hooks são funções Rust que fazem referência a ScopeState
(em um componente, você pode passar &cx
), e fornecem funcionalidade e estado.
use_state
use_state
é um dos hooks mais simples.
use_state
fornece o valor atual e uma maneira de atualizá-lo, definindo-o para outra coisause_state
faz o componente renderizar novamente e fornece o novo valorPor exemplo, você pode ter visto o exemplo do contador, no qual o estado (um número) é rastreado usando o hook use_state
:
{{#include ../../examples/hooks_counter.rs:component}}
Toda vez que o estado do componente muda, ele é renderizado novamente e a função do componente é chamada, para que você possa descrever como deseja que a nova interface do usuário se pareça. Você não precisa se preocupar em "mudar" nada - apenas descreva o que você quer em termos de estado, e Dioxus cuidará do resto!
use_state
retorna seu valor envolto em uma smart pointer do tipoUseState
. É por isso que você pode ler o valor e atualizá-lo, mesmo dentro de um manipulador.
Você pode usar vários hooks no mesmo componente se quiser:
{{#include ../../examples/hooks_counter_two_state.rs:component}}
O exemplo acima pode parecer um pouco mágico, já que as funções Rust normalmente não estão associadas ao estado. O Dioxus permite que os hooks mantenham o estado entre as renderizações através de uma referência a ScopeState
, e é por isso que você deve passar &cx
para eles.
Mas como Dioxus pode diferenciar entre vários hooks no mesmo componente? Como você viu no segundo exemplo, ambas as funções use_state
foram chamadas com os mesmos parâmetros, então como elas podem retornar coisas diferentes quando os contadores são diferentes?
{{#include ../../examples/hooks_counter_two_state.rs:use_state_calls}}
Isso só é possível porque os dois hooks são sempre chamados na mesma ordem, então Dioxus sabe qual é qual. Portanto, a ordem em que você chama os hooks é importante, e é por isso que você deve seguir certas regras ao usar os hooks:
use_
para que você não os confunda acidentalmente com funções normaisEssas regras significam que há certas coisas que você não pode fazer com hooks:
{{#include ../../examples/hooks_bad.rs:conditional}}
{{#include ../../examples/hooks_bad.rs:closure}}
{{#include ../../examples/hooks_bad.rs:loop}}
use_ref
use_state
é ótimo para rastrear valores simples. No entanto, você pode notar na UseState
API que a única maneira de modificar seu valor é substituí-lo por algo else (por exemplo, chamando set
, ou através de um dos operadores +=
, -=
). Isso funciona bem quando é barato construir um valor (como qualquer primitivo). Mas e se você quiser manter dados mais complexos no estado dos componentes?
Por exemplo, suponha que queremos manter um Vec
de valores. Se o armazenamos com use_state
, a única maneira de adicionar um novo valor à lista seria criar um novo Vec
com o valor adicional e colocá-lo no estado. Isto é custoso! Queremos modificar o Vec
existente.
Felizmente, existe outro hook para isso, use_ref
! É semelhante ao use_state
, mas permite obter uma referência mutável aos dados contidos.
Aqui está um exemplo simples que mantém uma lista de eventos em um use_ref
. Podemos adquirir acesso de escrita ao estado com .write()
, e então apenas .push
um novo valor para o estado:
{{#include ../../examples/hooks_use_ref.rs:component}}
Os valores de retorno de
use_state
euse_ref
, (UseState
eUseRef
, respectivamente) são de alguma forma semelhantes aCell
eRefCell
– eles fornecem mutabilidade interior. No entanto, esses wrappers do Dioxus também garantem que o componente seja renderizado novamente sempre que você alterar o estado.