Manipulando formulários com React

Continuando a nossa série de posts sobre como criar uma aplicação React, iremos ver neste post como podemos manipular dados em formulários usando React.

Utilizando e entendendo o state

O React é desenvolvido com a mentalidade de que não devemos manipular o DOM diretamente, como fazemos com o Jquery utilizando os métodos fornecidos em $, por exemplo. Para modificar qualquer elemento na tela, devemos associar o mesmo ao state, que nada mais é que um objeto criado na inicialização de todo componente React.

Utilizando o formulário que desenvolvemos, vamos aprimora-lo inicializando o objeto state com as variáveis que utilizaremos para buscar os valores preenchidos nos campos "Descrição" e "Valor". Para isso, declare o construtor da classe Form da seguinte maneira:

   constructor(props) {
      super(props);

      this.state = {
         description: "",
         ammount: 0
      };
   }

Assim como utilizaremos o objeto state para guardar informações referentes aos campos, o mesmo pode ser utilizado para os mais diversos propósitos, como guardar o resultado de um webservice, o resultado de alguma operação calculada, e quaisquer outros tipos de dados.

Amarrando os inputs com o objeto state

E no método render iremos atualizar os inputs de "Descrição" e "Valor" para que fiquem desta maneira:

<form action="">
  <div>
    <input
      type="text"
      placeholder="Descrição"
      value="{this.state.description}"
      onChange="{this.onDescriptionChange}"
    />
  </div>

  <div>
    <input
      type="text"
      placeholder="Valor"
      value="{this.state.value}"
      onChange="{this.onAmmountChange}"
    />
  </div>
  ...
</form>

Note que definir apenas o value do input não será o suficiente para que possamos manipular corretamente as informações que serão exibidas/mostradas para o usuário. No React devemos trabalhar apenas com o state, e por isso que definimos uma função dentro de cada propriedade onChange de ambos os inputs. Esta função será responsável por receber cada alteração de valor digitado pelo usuário e atualizar o objeto state do componente, que por consequência irá atualizar o valor do campo. Se não utilizarmos o método onChange, tudo que o usuário digitar. será ignorado.

Para atualizar o state com base nas informações recebidas, usaremos a função setState da seguinte maneira:

   onDescriptionChange(event) {
      this.setState({
         description: event.target.value
      });
   }

   onAmmountChange(event) {
      this.setState({
         ammount: event.target.value
      });
   }

Após salvar e atualizar a página, ao digitar algum valor nas caixas de texto, note que os métodos onDescriptionChange e onAmmountChange são chamados, porém um erro no console aparece uma ou mais vezes:

Cannot read property 'setState' of undefined

Isso acontece porque o contexto que o método onChange está sendo chamado, não é o mesmo contexto do componente que instanciamos no React. Caso tenha interesse em saber porque isso acontece com mais detalhes, recomendo que leia este post{:target="_blank"}

Para solucionar o problema, iremos utilizar o método bind do JavaScript, associando os métodos "onChange" que criamos, com o contexto da classe do componente Form.

   constructor(props) {
      super(props);

      this.state = {
         description: "",
         ammount: ""
      };

      this.onDescriptionChange = this.onDescriptionChange.bind(this);
      this.onAmmountChange = this.onAmmountChange.bind(this);
   }

Tratando o evento de click com o objeto state

Da mesma forma que usamos o onChange para a mudança de valor nos inputs do nosso formulário, usaremos o onClick para tratar o evento de click do botão de submit do nosso formulário. Para isso adicione a seguinte função ao componente Form:

   onSaveClick() {
      let transactions = localStorage.getItem('transactions');

      if (!transactions)
      transactions = [];

      let transaction = {
         description: this.state.description,
         ammount: this.state.ammount
      };

      transactions.push(transaction);

      localStorage.setItem('transactions', JSON.stringify(transactions));

      alert("Lançamento salvo com sucesso.");
   }

Como não temos um webservice onde essa informação possa ser salva, vamos utilizar o Local Storage do browser.

Antes de testarmos, defina o evento de onClick no elemento button dentro do método render:

<button onClick={this.onSaveClick}>Salvar</button>

E faça o bind da função no construtor da classe:

this.onSaveClick = this.onSaveClick.bind(this);

Até o próximo post!

Rodrigo Vargas ® 2021