Elementos HTML de formulário trabalham um pouco diferente dos outros elementos DOM no React, porque elementos de formulário mantém naturalmente algum estado interno. Por exemplo, este formulário em HTML puro aceita um único nome:
<form>
<label>
Name:
<input type="text" name="name" />
</label>
<input type="submit" value="Submit" />
</form>
Este formulário tem o comportamento padrão de um formulário HTML de navegar para uma nova página quando um usuário enviar o formulário. Se você quiser este comportamento no React, ele simplesmente funciona. Mas na maioria dos casos, é conveniente ter uma função JavaScript que manipula o envio do formulário e tem acesso ao que o usuário inseriu no formulário. A maneira padrão de conseguir isso é com uma técnica chamada "componentes controlados".
Em HTML, elementos de formulário como <input>
, <textarea>
, e <select>
tipicamente mantêm seus próprios estados e atualizam-nos baseado na entrada do usuário. Em React, estado mutável é tipicamente mantido na propriedade estado dos componentes, e só é atualizado com setState()
.
Nós podemos combinar os dois fazendo o estado do React ser a "única fonte de verdade". Em seguida o componente React que renderiza um formulário também controla o que acontece nesse formulário em uma entrada subsequente do usuário. Um elemento de formulário de entrada cujo valor é controlado pelo React dessa forma é chamado de "componente controlado".
Por exemplo, se nós quisermos fazer o exemplo anterior log o nome quando ele é enviado, nós podemos escrever o formulário como um componente controlado:
class NameForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: ''};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('A name was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<input type="text" value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Uma vez que o atributo value
é definido no nosso elemento do formulário, o valor exibido sempre será this.state.value
, tornando o estado do React a fonte de verdade. Como o handleChange
executa em cada pressionamento de tecla para atualizar o estado do React, o valor exibido será atualizado conforme o que o usuário digita.
Com um componente controlado, cada mutação de estado terá uma função de manipulação associada. Isto torna fácil modificar ou validar a entrada do usuário. Por exemplo, se você quer forçar que os nomes sejam escritos com todas as letras maiúsculas, poderíamos escrever o código em handleChange
como:
handleChange(event) {
this.setState({value: event.target.value.toUpperCase()});
}
Em HTML, o elemento <textarea>
define seu texto pelos seus filhos:
<textarea>
Hello there, this is some text in a text area
</textarea>
Em React, um <textarea>
usa um atributo em vez disso. Desta forma, um formulário usando um <textarea>
pode ser escrito de forma muito semelhante a um formulário que usa um input de linha única:
class EssayForm extends React.Component {
constructor(props) {
super(props);
this.state = {
value: 'Please write an essay about your favorite DOM element.'
};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('An essay was submitted: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Name:
<textarea value={this.state.value} onChange={this.handleChange} />
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Observe que this.state.value
é inicializado no construtor, então o textarea começå com algum texto nele.
Em HTML, <select>
cria uma lista drop-down. Por exemplo, este HTML cria uma lista drop-down de sabores:
<select>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option selected value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
Observe que a opção Coconut é inicializada selecionada por causa do atributo selected
. React, ao invés de usar o atributo selected
, usa um atributo value
na raiz da tag select
. Isto é mais conveniente em um componente controlado porque você só precisa atualizar em um lugar. Por exemplo:
class FlavorForm extends React.Component {
constructor(props) {
super(props);
this.state = {value: 'coconut'};
this.handleChange = this.handleChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
handleChange(event) {
this.setState({value: event.target.value});
}
handleSubmit(event) {
alert('Your favorite flavor is: ' + this.state.value);
event.preventDefault();
}
render() {
return (
<form onSubmit={this.handleSubmit}>
<label>
Pick your favorite La Croix flavor:
<select value={this.state.value} onChange={this.handleChange}>
<option value="grapefruit">Grapefruit</option>
<option value="lime">Lime</option>
<option value="coconut">Coconut</option>
<option value="mango">Mango</option>
</select>
</label>
<input type="submit" value="Submit" />
</form>
);
}
}
Em geral, isto faz com que <input type="text">
, <textarea>
e <select>
trabalhem de forma similar. Todos eles aceitam um atributo value
que você pode usar para implementar um componente controlado.
As vezes pode ser tedioso usar componentes controlados, porque você precisa escrever um evento manipulador para cada maneira que os dados podem mudar e centralizar todo o estado através de um componente React. Isto pode se tornar particularmente irritante quando está convertendo uma base de código preexistente para React, ou integrando uma aplicação React com uma biblioteca não React. Nessas situações, você pode querer olhar componentes não controlados, uma técnica alternativa para implementar entradas de formulários.