O estado é usado para organizar o monitoramento de dados dos aplicativos React. Os estados mudam à medida que os usuários interagem com os aplicativos. Quando o usuário executa alguma ação, precisamos atualizar o estado, que é um conjunto de dados com base no qual o que o usuário vê na tela é formado. Atualize o estado dos aplicativos React usando o método
setState .

Como os estados não devem ser atualizados diretamente (em React, o estado deve ser imutável), com a complicação da estrutura dos estados, trabalhar com eles se transforma em uma tarefa não trivial. Nomeadamente, fica difícil para o programador navegar no estado e usar seus dados no aplicativo.
Nessas situações, você pode usar a biblioteca Immer. Seu uso nas aplicações React é dedicado ao material, cuja tradução publicamos hoje.
Noções básicas de uso do Immer em aplicativos de reação
Ao usar o Immer, a estrutura de estado de um aplicativo React pode ser simplificada, o que significa que será mais fácil trabalhar com ele. Immer usa o conceito de "rascunho". Um "rascunho" pode ser tomado como uma cópia do estado, mas não o próprio estado.
O Immer, por assim dizer, copia o estado “pressionando” as teclas CMD + C e, usando as teclas CMD + V, insere o que ele copiou em um local onde os dados copiados podem ser visualizados sem perturbar os materiais originais. A alteração dos dados incluídos no estado é feita no "rascunho", após o qual, com base nas alterações feitas no "rascunho", o estado atual do aplicativo é atualizado.
Suponha que o estado do seu aplicativo tenha esta aparência:
this.state = { name: 'Kunle', age: 30, city: 'Lagos', country: 'Nigeria' }
Aqui estão os dados do usuário. Este usuário, como se viu, está comemorando seu 31º aniversário. Isso significa que precisamos atualizar sua idade (propriedade de
age
). Se você usar o Immer para resolver esse problema, uma cópia desse estado será criada primeiro.
Agora imagine que uma cópia da fortuna foi feita, foi entregue ao correio e ele a entregou a Kunle. Isso significa que agora existem duas cópias do estado. Um deles é o estado atual do aplicativo e o segundo é uma cópia "aproximada" que foi transferida para o usuário. O usuário, editando o "rascunho", muda sua idade para 31. Depois disso, o correio retorna com o documento alterado e entrega o "rascunho" ao aplicativo. Lá, é feita uma comparação de duas versões do documento e apenas são feitas alterações em relação à idade do usuário no estado atual do aplicativo, pois nada mudou no rascunho.
Esse esquema de trabalho não viola a idéia de imunidade estatal - o estado atual não é diretamente atualizado. Em geral, podemos dizer que o uso de Immer simplesmente ajuda a melhorar a usabilidade do estado imunológico.
Exemplo nº 1: semáforo
Vamos dar uma olhada em um exemplo de aplicativo de trabalho que usa o Immer. Suponha que você esteja desenvolvendo um aplicativo de semáforo. Nesta aplicação, você pode tentar usar o Immer.
Aqui está a aparência da tela deste aplicativo em um dos momentos de sua operação.
Aplicação de semáforoAqui você pode encontrar o código do projeto.
Aqui está a aparência do componente, já que o projeto usa o Immer.
const {produce} = immer class App extends React.Component { state = { red: 'red', yellow: 'black', green: 'black', next: "yellow" } componentDidMount() { this.interval = setInterval(() => this.changeHandle(), 3000); } componentWillUnmount() { clearInterval(this.interval); } handleRedLight = () => { this.setState( produce(draft => { draft.red = 'red'; draft.yellow = 'black'; draft.green = 'black'; draft.next = 'yellow' }) ) } handleYellowLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'yellow'; draft.green = 'black'; draft.next = 'green' }) ) } handleGreenLight = () => { this.setState( produce(draft => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }) ) } changeHandle = () => { if (this.state.next === 'yellow') { this.handleYellowLight() } else if (this.state.next === 'green') { this.handleGreenLight() } else { this.handleRedLight() } } render() { return ( <div className="box"> <div className="circle" style={{backgroundColor: this.state.red}}></div> <div className="circle" style={{backgroundColor: this.state.yellow}}></div> <div className="circle" style={{backgroundColor: this.state.green}}></div> </div> ); } };
Produce
é um recurso padrão importado do Immer. Passamos, como um valor, para o método
setState()
. A função
produce
assume uma função que, como argumento, aceita
draft
. É nessa função que podemos editar o estado "rascunho", trazendo-o para a forma que deve assumir um estado real.
Se tudo isso parece muito complicado para você - aqui está outra abordagem para escrever código que resolve as mesmas tarefas que o código acima. Primeiro, crie uma função:
const handleLight = (state) => { return produce(state, (draft) => { draft.red = 'black'; draft.yellow = 'black'; draft.green = 'green'; draft.next = 'red' }); }
Para a função
produce
, como argumentos, passamos o estado atual do aplicativo e outra função que recebe o argumento de
draft
. Agora vamos aproveitar tudo isso no componente:
handleGreenLight = () => { const nextState = handleLight(this.state) this.setState(nextState) }
Exemplo 2: lista de compras
Se você trabalha com o React há algum tempo, não deve se surpreender com a
sintaxe de propagação . Ao usar o Immer, você não precisa usar modelos semelhantes. Em particular, ao trabalhar com matrizes contidas em um estado.
Continuaremos a explorar as possibilidades do Immer, criando um aplicativo que implementa uma lista de compras.
Lista de comprasAqui você pode experimentar.
Aqui está o componente com o qual estamos trabalhando.
class App extends React.Component { constructor(props) { super(props) this.state = { item: "", price: 0, list: [ { id: 1, name: "Cereals", price: 12 }, { id: 2, name: "Rice", price: 10 } ] } } handleInputChange = e => { this.setState( produce(draft => { draft[event.target.name] = event.target.value })) } handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }) ) }; render() { return ( <React.Fragment> <section className="section"> <div className="box"> <form onSubmit={this.handleSubmit}> <h2>Create your shopping list</h2> <div> <input type="text" placeholder="Item's Name" onChange={this.handleInputChange} name="name" className="input" /> </div> <div> <input type="number" placeholder="Item's Price" onChange={this.handleInputChange} name="price" className="input" /> </div> <button className="button is-grey">Submit</button> </form> </div> <div className="box"> { this.state.list.length ? ( this.state.list.map(item => ( <ul> <li key={item.id}> <p>{item.name}</p> <p>${item.price}</p> </li> <hr /> </ul> )) ) : <p>Your list is empty</p> } </div> </section> </React.Fragment> ) } } ReactDOM.render( <App />, document.getElementById('root') );
Ao adicionar novas notas de compras à lista, precisamos atualizar o estado do componente no qual, na matriz da
list
, novos elementos devem ser salvos. Para atualizar o item da
list
usando o método
setState()
,
setState()
precisa do seguinte código:
handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState({ list: [...this.state.list, newItem] }) };
Se durante a operação do aplicativo você precisar atualizar muitos elementos de estado - a sintaxe de propagação terá que ser usada com muita frequência. Um novo estado é obtido combinando o que já está no estado com novos dados. À medida que o número de mudanças aumenta, o trabalho se torna mais complicado. Se usar Immer - essas coisas não causam dificuldades. Você pode verificar isso consultando o código de exemplo no início desta seção.
Mas e se quisermos adicionar uma função ao projeto que, na forma de um retorno de chamada, será chamado após a atualização do estado? Por exemplo, isso pode ser necessário se você precisar contar o número de entradas na lista ou o custo total de todas as compras planejadas.
Aqui você pode dar uma olhada no código do aplicativo, que iremos analisar agora. Sua interface é mostrada abaixo.
Aplicativo com a função de calcular o custo total das compras planejadasPortanto, suponha que desejamos calcular o valor total das compras planejadas. Vamos começar criando um mecanismo de atualização de estado. Este mecanismo é representado pela função
handleSubmit
:
handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuid.v4(), name: this.state.name, price: this.state.price } this.setState( produce(draft => { draft.list = draft.list.concat(newItem) }), () => { this.calculateAmount(this.state.list) } ) };
Na função
handleSubmit
primeiro criamos um objeto com base nos dados inseridos pelo usuário. A referência ao objeto é gravada na constante
newItem
. Para formar um novo estado do aplicativo, o método
.concat()
é usado. Esse método, chamado em uma matriz, retorna uma nova matriz, que inclui elementos da matriz original, bem como um novo elemento. A nova matriz é gravada em
draft.list
. Depois disso, o Immer pode atualizar o estado do aplicativo.
O retorno de chamada, a função
calculateAmount
, é chamado após uma atualização de estado. É importante observar que essa função usa uma versão atualizada do estado.
A função
calculateAmount
terá a seguinte aparência:
calculateAmount = (list) => { let total = 0; for (let i = 0; i < list.length; i++) { total += parseInt(list[i].price, 10) } this.setState( produce(draft => { draft.totalAmount = total }) ) }
Hooks immer
Use-immer é um gancho que permite que um desenvolvedor controle o estado dos aplicativos React. Vejamos como esse gancho funciona implementando o aplicativo de contador clássico em sua base:
import React from "react"; import {useImmer} from "use-immer"; const Counter = () => { const [count, updateCounter] = useImmer({ value: 0 }); function increment() { updateCounter(draft => { draft.value = draft.value +1; }); } return ( <div> <h1> Counter {count.value} </h1> <br /> <button onClick={increment}>Increment</button> </div> ); } export default Counter;
A função
useImmer
muito semelhante ao método
useState . A função retorna um estado e uma função que atualiza o estado. Quando o componente é carregado pela primeira vez, o conteúdo do estado (nesse caso, a propriedade
count
) corresponde ao valor passado para
useImmer
. O uso da função retornada para atualizar o estado nos permite criar uma função de incremento que incrementa o valor da propriedade do estado da
count
.
E aqui está o código que usa o gancho para o Immer, remanescente do
useReducer :
import React, { useRef } from "react"; import {useImmerReducer } from "use-immer"; import uuidv4 from "uuid/v4" const initialState = []; const reducer = (draft, action) => { switch (action.type) { case "ADD_ITEM": draft.push(action.item); return; case "CLEAR_LIST": return initialState; default: return draft; } } const Todo = () => { const inputEl = useRef(null); const [state, dispatch] = useImmerReducer(reducer, initialState); const handleSubmit = (e) => { e.preventDefault() const newItem = { id: uuidv4(), text: inputEl.current.value }; dispatch({ type: "ADD_ITEM", item: newItem }); inputEl.current.value = ""; inputEl.current.focus(); } const handleClear = () => { dispatch({ type: 'CLEAR_LIST' }) } return ( <div className='App'> <header className='App-header'> <ul> {state.map(todo => { return <li key={todo.id}>{todo.text}</li>; })} </ul> <form onSubmit={handleSubmit}> <input type='text' ref={inputEl} /> <button type='submit' > Add Todo </button> </form> <button onClick={handleClear} > Clear Todos </button> </header> </div> ); } export default Todo;
A função
useImmerReducer
aceita a função
useImmerReducer
e o estado inicial. Retorna a função de estado e
dispatch
. Depois disso, você pode ignorar o estado para exibir os elementos nele. As ações de envio usando a função de
dispatch
são executadas quando um novo item é adicionado à lista de tarefas e quando a lista é limpa. A ação a ser enviada recebe um tipo com base no qual uma decisão é tomada na função redutora sobre o que exatamente precisa ser feito para processar uma ação específica.
No redutor, usamos, como antes, uma entidade de
draft
, não um
state
. Graças a isso, temos uma maneira conveniente de gerenciar o estado do aplicativo.
O código usado no exemplo anterior pode ser encontrado
aqui .
Sumário
Neste artigo, falamos sobre o Immer, uma biblioteca que simplifica o gerenciamento do estado dos aplicativos React. O autor do artigo acredita que todos os interessados nesta biblioteca podem usar o Immer em seus novos aplicativos ou introduzi-lo lentamente em um dos projetos atuais.
Aqui está o material onde você pode encontrar alguns detalhes sobre o Immer.
Caros leitores! Você planeja usar o Immer?
