1
2
Tipos de Inputs Controlados
Implemente diferentes tipos de campos de formulário
Tipos de Inputs Controlados
Input de Texto
function FormularioTexto() {
const [formData, setFormData] = useState({
nome: '',
email: '',
telefone: '',
senha: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setFormData(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<input
type="text"
name="nome"
value={formData.nome}
onChange={handleChange}
placeholder="Nome completo"
/>
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
placeholder="Email"
/>
<input
type="tel"
name="telefone"
value={formData.telefone}
onChange={handleChange}
placeholder="Telefone"
/>
<input
type="password"
name="senha"
value={formData.senha}
onChange={handleChange}
placeholder="Senha"
/>
</form>
);
}
Textarea
function FormularioComentario() {
const [comentario, setComentario] = useState('');
const caracteresRestantes = 500 - comentario.length;
return (
<div>
<textarea
value={comentario}
onChange={(e) => setComentario(e.target.value)}
placeholder="Deixe seu comentário..."
rows="5"
cols="50"
maxLength="500"
/>
<p>Caracteres restantes: {caracteresRestantes}</p>
</div>
);
}
Select
function FormularioSelect() {
const [pais, setPais] = useState('');
const [estado, setEstado] = useState('');
const estados = {
brasil: ['SP', 'RJ', 'MG', 'BA'],
eua: ['NY', 'CA', 'TX', 'FL'],
canada: ['ON', 'QC', 'BC', 'AB']
};
return (
<form>
<select value={pais} onChange={(e) => setPais(e.target.value)}>
<option value="">Selecione um país</option>
<option value="brasil">Brasil</option>
<option value="eua">Estados Unidos</option>
<option value="canada">Canadá</option>
</select>
{pais && (
<select value={estado} onChange={(e) => setEstado(e.target.value)}>
<option value="">Selecione um estado</option>
{estados[pais].map(est => (
<option key={est} value={est}>{est}</option>
))}
</select>
)}
</form>
);
}
Radio Buttons
function FormularioRadio() {
const [plano, setPlano] = useState('basico');
const [pagamento, setPagamento] = useState('mensal');
const planos = [
{ id: 'basico', nome: 'Básico', preco: 29.90 },
{ id: 'pro', nome: 'Pro', preco: 59.90 },
{ id: 'empresa', nome: 'Empresa', preco: 99.90 }
];
return (
<form>
<fieldset>
<legend>Escolha seu plano:</legend>
{planos.map(p => (
<label key={p.id}>
<input
type="radio"
name="plano"
value={p.id}
checked={plano === p.id}
onChange={(e) => setPlano(e.target.value)}
/>
{p.nome} - R$ {p.preco}/mês
</label>
))}
</fieldset>
<fieldset>
<legend>Forma de pagamento:</legend>
<label>
<input
type="radio"
name="pagamento"
value="mensal"
checked={pagamento === 'mensal'}
onChange={(e) => setPagamento(e.target.value)}
/>
Mensal
</label>
<label>
<input
type="radio"
name="pagamento"
value="anual"
checked={pagamento === 'anual'}
onChange={(e) => setPagamento(e.target.value)}
/>
Anual (20% desconto)
</label>
</fieldset>
</form>
);
}
Checkboxes
Checkbox Único
function FormularioCheckbox() {
const [aceiteTermos, setAceiteTermos] = useState(false);
const [receberEmails, setReceberEmails] = useState(false);
return (
<form>
<label>
<input
type="checkbox"
checked={aceiteTermos}
onChange={(e) => setAceiteTermos(e.target.checked)}
/>
Aceito os termos e condições
</label>
<label>
<input
type="checkbox"
checked={receberEmails}
onChange={(e) => setReceberEmails(e.target.checked)}
/>
Desejo receber emails promocionais
</label>
</form>
);
}
Múltiplos Checkboxes
function FormularioInteresses() {
const [interesses, setInteresses] = useState([]);
const opcoes = [
'JavaScript',
'React',
'Node.js',
'Python',
'Machine Learning',
'DevOps'
];
const handleCheckboxChange = (opcao) => {
setInteresses(prev => {
if (prev.includes(opcao)) {
return prev.filter(item => item !== opcao);
} else {
return [...prev, opcao];
}
});
};
return (
<form>
<h3>Selecione seus interesses:</h3>
{opcoes.map(opcao => (
<label key={opcao}>
<input
type="checkbox"
checked={interesses.includes(opcao)}
onChange={() => handleCheckboxChange(opcao)}
/>
{opcao}
</label>
))}
<p>Selecionados: {interesses.join(', ') || 'Nenhum'}</p>
</form>
);
}
Range Slider
function FormularioRange() {
const [idade, setIdade] = useState(25);
const [experiencia, setExperiencia] = useState(5);
const [salario, setSalario] = useState(5000);
return (
<form>
<label>
Idade: {idade} anos
<input
type="range"
min="18"
max="65"
value={idade}
onChange={(e) => setIdade(e.target.value)}
/>
</label>
<label>
Experiência: {experiencia} anos
<input
type="range"
min="0"
max="30"
value={experiencia}
onChange={(e) => setExperiencia(e.target.value)}
/>
</label>
<label>
Salário esperado: R$ {salario}
<input
type="range"
min="1000"
max="20000"
step="500"
value={salario}
onChange={(e) => setSalario(e.target.value)}
/>
</label>
</form>
);
}
Date e Time
function FormularioDataHora() {
const [evento, setEvento] = useState({
data: '',
hora: '',
dataHora: ''
});
const handleChange = (e) => {
const { name, value } = e.target;
setEvento(prev => ({
...prev,
[name]: value
}));
};
return (
<form>
<label>
Data do evento:
<input
type="date"
name="data"
value={evento.data}
onChange={handleChange}
min={new Date().toISOString().split('T')[0]}
/>
</label>
<label>
Hora do evento:
<input
type="time"
name="hora"
value={evento.hora}
onChange={handleChange}
/>
</label>
<label>
Data e hora completa:
<input
type="datetime-local"
name="dataHora"
value={evento.dataHora}
onChange={handleChange}
/>
</label>
</form>
);
}
Color Picker
function FormularioCor() {
const [cores, setCores] = useState({
primaria: '#1976d2',
secundaria: '#dc004e',
fundo: '#ffffff'
});
const handleChange = (e) => {
const { name, value } = e.target;
setCores(prev => ({
...prev,
[name]: value
}));
};
return (
<div>
<form>
<label>
Cor primária:
<input
type="color"
name="primaria"
value={cores.primaria}
onChange={handleChange}
/>
</label>
<label>
Cor secundária:
<input
type="color"
name="secundaria"
value={cores.secundaria}
onChange={handleChange}
/>
</label>
<label>
Cor de fundo:
<input
type="color"
name="fundo"
value={cores.fundo}
onChange={handleChange}
/>
</label>
</form>
<div style={{
backgroundColor: cores.fundo,
padding: '20px',
marginTop: '20px'
}}>
<h2 style={{ color: cores.primaria }}>Título Principal</h2>
<p style={{ color: cores.secundaria }}>Texto secundário</p>
</div>
</div>
);
}
Formatação de Inputs
function FormularioFormatado() {
const [telefone, setTelefone] = useState('');
const [cpf, setCpf] = useState('');
const [cartao, setCartao] = useState('');
const formatarTelefone = (valor) => {
const numero = valor.replace(/\D/g, '');
if (numero.length <= 11) {
const formatado = numero.replace(
/(\d{2})(\d{5})(\d{4})/,
'($1) $2-$3'
);
setTelefone(formatado);
}
};
const formatarCPF = (valor) => {
const numero = valor.replace(/\D/g, '');
if (numero.length <= 11) {
const formatado = numero.replace(
/(\d{3})(\d{3})(\d{3})(\d{2})/,
'$1.$2.$3-$4'
);
setCpf(formatado);
}
};
const formatarCartao = (valor) => {
const numero = valor.replace(/\D/g, '');
if (numero.length <= 16) {
const formatado = numero.replace(
/(\d{4})(\d{4})(\d{4})(\d{4})/,
'$1 $2 $3 $4'
);
setCartao(formatado);
}
};
return (
<form>
<input
type="text"
value={telefone}
onChange={(e) => formatarTelefone(e.target.value)}
placeholder="(11) 98765-4321"
/>
<input
type="text"
value={cpf}
onChange={(e) => formatarCPF(e.target.value)}
placeholder="123.456.789-00"
/>
<input
type="text"
value={cartao}
onChange={(e) => formatarCartao(e.target.value)}
placeholder="1234 5678 9012 3456"
/>
</form>
);
}
3
Exercício: Formulário de Cadastro
Crie um formulário de cadastro completo com vários tipos de campos
Activity
Exercício: Formulário de Cadastro
Objetivo
Criar um formulário de cadastro de usuário completo com diferentes tipos de campos e validação básica.
Requisitos
-
Campos Obrigatórios
- Nome completo (texto)
- Email (email)
- Senha (password)
- Confirmar senha (password)
- Data de nascimento (date)
- Gênero (radio)
- País (select)
- Aceitar termos (checkbox)
-
Campos Opcionais
- Telefone (tel)
- Bio (textarea)
- Interesses (múltiplos checkboxes)
- Nível de experiência (range)
-
Funcionalidades
- Mostrar/ocultar senha
- Validação de senha (mínimo 8 caracteres)
- Verificar se senhas coincidem
- Botão submit desabilitado até aceitar termos
- Mostrar dados ao submeter
Template Inicial
import React, { useState } from 'react';
function FormularioCadastro() {
const [formData, setFormData] = useState({
nome: '',
email: '',
senha: '',
confirmarSenha: '',
dataNascimento: '',
genero: '',
pais: '',
telefone: '',
bio: '',
interesses: [],
experiencia: 1,
aceitarTermos: false
});
const [mostrarSenha, setMostrarSenha] = useState(false);
const [erros, setErros] = useState({});
// Seu código aqui
return (
<form>
{/* Implemente o formulário aqui */}
</form>
);
}
export default FormularioCadastro;
Dicas
- Use um único handler para todos os campos quando possível
- Trate checkboxes e campos de array separadamente
- Valide em tempo real para melhor UX
- Use fieldsets para agrupar campos relacionados
Solução
Clique para ver a solução
import React, { useState } from 'react';
function FormularioCadastro() {
const [formData, setFormData] = useState({
nome: '',
email: '',
senha: '',
confirmarSenha: '',
dataNascimento: '',
genero: '',
pais: '',
telefone: '',
bio: '',
interesses: [],
experiencia: 1,
aceitarTermos: false
});
const [mostrarSenha, setMostrarSenha] = useState(false);
const [erros, setErros] = useState({});
const [enviado, setEnviado] = useState(false);
const paises = ['Brasil', 'Portugal', 'Estados Unidos', 'Canadá', 'Argentina'];
const interessesOpcoes = ['Tecnologia', 'Esportes', 'Música', 'Viagens', 'Leitura', 'Culinária'];
const handleChange = (e) => {
const { name, value, type, checked } = e.target;
if (type === 'checkbox' && name === 'aceitarTermos') {
setFormData(prev => ({
...prev,
[name]: checked
}));
} else if (name === 'interesses') {
setFormData(prev => ({
...prev,
interesses: checked
? [...prev.interesses, value]
: prev.interesses.filter(item => item !== value)
}));
} else {
setFormData(prev => ({
...prev,
[name]: value
}));
}
// Limpar erro do campo quando usuário começa a digitar
if (erros[name]) {
setErros(prev => {
const newErros = { ...prev };
delete newErros[name];
return newErros;
});
}
};
const validarFormulario = () => {
const novosErros = {};
if (!formData.nome.trim()) {
novosErros.nome = 'Nome é obrigatório';
}
if (!formData.email.includes('@')) {
novosErros.email = 'Email inválido';
}
if (formData.senha.length < 8) {
novosErros.senha = 'Senha deve ter no mínimo 8 caracteres';
}
if (formData.senha !== formData.confirmarSenha) {
novosErros.confirmarSenha = 'Senhas não coincidem';
}
if (!formData.dataNascimento) {
novosErros.dataNascimento = 'Data de nascimento é obrigatória';
}
if (!formData.genero) {
novosErros.genero = 'Selecione um gênero';
}
if (!formData.pais) {
novosErros.pais = 'Selecione um país';
}
if (!formData.aceitarTermos) {
novosErros.aceitarTermos = 'Você deve aceitar os termos';
}
setErros(novosErros);
return Object.keys(novosErros).length === 0;
};
const handleSubmit = (e) => {
e.preventDefault();
if (validarFormulario()) {
setEnviado(true);
console.log('Formulário enviado:', formData);
}
};
if (enviado) {
return (
<div style={{ padding: '20px', backgroundColor: '#e8f5e9', borderRadius: '8px' }}>
<h2>Cadastro realizado com sucesso!</h2>
<h3>Dados enviados:</h3>
<pre>{JSON.stringify(formData, null, 2)}</pre>
<button onClick={() => {
setEnviado(false);
setFormData({
nome: '',
email: '',
senha: '',
confirmarSenha: '',
dataNascimento: '',
genero: '',
pais: '',
telefone: '',
bio: '',
interesses: [],
experiencia: 1,
aceitarTermos: false
});
}}>
Novo Cadastro
</button>
</div>
);
}
return (
<form onSubmit={handleSubmit} style={{ maxWidth: '600px', margin: '0 auto' }}>
<h1>Formulário de Cadastro</h1>
<fieldset>
<legend>Informações Pessoais</legend>
<div>
<label>
Nome Completo *
<input
type="text"
name="nome"
value={formData.nome}
onChange={handleChange}
style={{ width: '100%' }}
/>
</label>
{erros.nome && <span style={{ color: 'red' }}>{erros.nome}</span>}
</div>
<div>
<label>
Email *
<input
type="email"
name="email"
value={formData.email}
onChange={handleChange}
style={{ width: '100%' }}
/>
</label>
{erros.email && <span style={{ color: 'red' }}>{erros.email}</span>}
</div>
<div>
<label>
Senha *
<div style={{ position: 'relative' }}>
<input
type={mostrarSenha ? 'text' : 'password'}
name="senha"
value={formData.senha}
onChange={handleChange}
style={{ width: '100%' }}
/>
<button
type="button"
onClick={() => setMostrarSenha(!mostrarSenha)}
style={{ position: 'absolute', right: '5px', top: '5px' }}
>
{mostrarSenha ? 'Ocultar' : 'Mostrar'}
</button>
</div>
</label>
{erros.senha && <span style={{ color: 'red' }}>{erros.senha}</span>}
</div>
<div>
<label>
Confirmar Senha *
<input
type="password"
name="confirmarSenha"
value={formData.confirmarSenha}
onChange={handleChange}
style={{ width: '100%' }}
/>
</label>
{erros.confirmarSenha && <span style={{ color: 'red' }}>{erros.confirmarSenha}</span>}
</div>
<div>
<label>
Data de Nascimento *
<input
type="date"
name="dataNascimento"
value={formData.dataNascimento}
onChange={handleChange}
max={new Date().toISOString().split('T')[0]}
/>
</label>
{erros.dataNascimento && <span style={{ color: 'red' }}>{erros.dataNascimento}</span>}
</div>
<div>
<label>
Telefone
<input
type="tel"
name="telefone"
value={formData.telefone}
onChange={handleChange}
placeholder="(11) 98765-4321"
/>
</label>
</div>
</fieldset>
<fieldset>
<legend>Gênero *</legend>
<label>
<input
type="radio"
name="genero"
value="masculino"
checked={formData.genero === 'masculino'}
onChange={handleChange}
/>
Masculino
</label>
<label>
<input
type="radio"
name="genero"
value="feminino"
checked={formData.genero === 'feminino'}
onChange={handleChange}
/>
Feminino
</label>
<label>
<input
type="radio"
name="genero"
value="outro"
checked={formData.genero === 'outro'}
onChange={handleChange}
/>
Outro
</label>
{erros.genero && <span style={{ color: 'red' }}>{erros.genero}</span>}
</fieldset>
<fieldset>
<legend>Localização</legend>
<label>
País *
<select
name="pais"
value={formData.pais}
onChange={handleChange}
style={{ width: '100%' }}
>
<option value="">Selecione um país</option>
{paises.map(pais => (
<option key={pais} value={pais}>{pais}</option>
))}
</select>
</label>
{erros.pais && <span style={{ color: 'red' }}>{erros.pais}</span>}
</fieldset>
<fieldset>
<legend>Sobre Você</legend>
<label>
Bio
<textarea
name="bio"
value={formData.bio}
onChange={handleChange}
rows="4"
style={{ width: '100%' }}
placeholder="Fale um pouco sobre você..."
/>
</label>
<div>
<label>
Nível de Experiência: {formData.experiencia}
<input
type="range"
name="experiencia"
min="1"
max="10"
value={formData.experiencia}
onChange={handleChange}
style={{ width: '100%' }}
/>
</label>
</div>
</fieldset>
<fieldset>
<legend>Interesses</legend>
{interessesOpcoes.map(interesse => (
<label key={interesse}>
<input
type="checkbox"
name="interesses"
value={interesse}
checked={formData.interesses.includes(interesse)}
onChange={handleChange}
/>
{interesse}
</label>
))}
</fieldset>
<div style={{ margin: '20px 0' }}>
<label>
<input
type="checkbox"
name="aceitarTermos"
checked={formData.aceitarTermos}
onChange={handleChange}
/>
Aceito os termos e condições *
</label>
{erros.aceitarTermos && <span style={{ color: 'red' }}>{erros.aceitarTermos}</span>}
</div>
<button
type="submit"
disabled={!formData.aceitarTermos}
style={{
padding: '10px 20px',
backgroundColor: formData.aceitarTermos ? '#4caf50' : '#ccc',
color: 'white',
border: 'none',
borderRadius: '4px',
cursor: formData.aceitarTermos ? 'pointer' : 'not-allowed'
}}
>
Cadastrar
</button>
</form>
);
}
export default FormularioCadastro;
Desafios Extras
- Validação de CPF: Adicione campo e validação de CPF
- Upload de foto: Adicione campo para foto de perfil
- Endereço completo: CEP com busca automática
- Força da senha: Indicador visual de força
- Confirmação por email: Simular envio de email
3 content items