Validación de Formularios
Volver a clases
Desarrollo Móvil●●Intermedio

Validación de Formularios

120 min
26 vistas

Validaciones manuales, manejo de errores, estados touched y feedback visual.

Validación Manual

typescript
1interface FormData {
2  name: string;
3  email: string;
4  password: string;
5  confirmPassword: string;
6}
7
8interface FormErrors {
9  name?: string;
10  email?: string;
11  password?: string;
12  confirmPassword?: string;
13}
14
15function RegisterForm() {
16  const [formData, setFormData] = useState<FormData>({
17    name: "",
18    email: "",
19    password: "",
20    confirmPassword: "",
21  });
22
23  const [errors, setErrors] = useState<FormErrors>({});
24  const [touched, setTouched] = useState<Record<string, boolean>>({});
25
26  const validate = (): FormErrors => {
27    const newErrors: FormErrors = {};
28
29    // Validar nombre
30    if (!formData.name.trim()) {
31      newErrors.name = "El nombre es requerido";
32    } else if (formData.name.length < 3) {
33      newErrors.name = "El nombre debe tener al menos 3 caracteres";
34    }
35
36    // Validar email
37    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
38    if (!formData.email) {
39      newErrors.email = "El email es requerido";
40    } else if (!emailRegex.test(formData.email)) {
41      newErrors.email = "Email inválido";
42    }
43
44    // Validar contraseña
45    if (!formData.password) {
46      newErrors.password = "La contraseña es requerida";
47    } else if (formData.password.length < 6) {
48      newErrors.password = "Mínimo 6 caracteres";
49    } else if (!/(?=.*[0-9])/.test(formData.password)) {
50      newErrors.password = "Debe contener al menos un número";
51    }
52
53    // Validar confirmación
54    if (formData.password !== formData.confirmPassword) {
55      newErrors.confirmPassword = "Las contraseñas no coinciden";
56    }
57
58    return newErrors;
59  };
60
61  const handleChange = (field: keyof FormData) => (value: string) => {
62    setFormData({ ...formData, [field]: value });
63
64    // Limpiar error al escribir
65    if (errors[field]) {
66      setErrors({ ...errors, [field]: undefined });
67    }
68  };
69
70  const handleBlur = (field: keyof FormData) => () => {
71    setTouched({ ...touched, [field]: true });
72
73    // Validar campo específico
74    const fieldErrors = validate();
75    if (fieldErrors[field]) {
76      setErrors({ ...errors, [field]: fieldErrors[field] });
77    }
78  };
79
80  const handleSubmit = () => {
81    const validationErrors = validate();
82
83    if (Object.keys(validationErrors).length > 0) {
84      setErrors(validationErrors);
85      setTouched({
86        name: true,
87        email: true,
88        password: true,
89        confirmPassword: true,
90      });
91      return;
92    }
93
94    // Enviar formulario
95    console.log("Formulario válido:", formData);
96  };
97
98  return (
99    <View style={styles.container}>
100      <View style={styles.inputContainer}>
101        <TextInput
102          style={[
103            styles.input,
104            touched.name && errors.name && styles.inputError,
105          ]}
106          value={formData.name}
107          onChangeText={handleChange("name")}
108          onBlur={handleBlur("name")}
109          placeholder="Nombre completo"
110        />
111        {touched.name && errors.name && (
112          <Text style={styles.errorText}>{errors.name}</Text>
113        )}
114      </View>
115
116      <View style={styles.inputContainer}>
117        <TextInput
118          style={[
119            styles.input,
120            touched.email && errors.email && styles.inputError,
121          ]}
122          value={formData.email}
123          onChangeText={handleChange("email")}
124          onBlur={handleBlur("email")}
125          placeholder="Email"
126          keyboardType="email-address"
127          autoCapitalize="none"
128        />
129        {touched.email && errors.email && (
130          <Text style={styles.errorText}>{errors.email}</Text>
131        )}
132      </View>
133
134      <View style={styles.inputContainer}>
135        <TextInput
136          style={[
137            styles.input,
138            touched.password && errors.password && styles.inputError,
139          ]}
140          value={formData.password}
141          onChangeText={handleChange("password")}
142          onBlur={handleBlur("password")}
143          placeholder="Contraseña"
144          secureTextEntry
145        />
146        {touched.password && errors.password && (
147          <Text style={styles.errorText}>{errors.password}</Text>
148        )}
149      </View>
150
151      <View style={styles.inputContainer}>
152        <TextInput
153          style={[
154            styles.input,
155            touched.confirmPassword &&
156              errors.confirmPassword &&
157              styles.inputError,
158          ]}
159          value={formData.confirmPassword}
160          onChangeText={handleChange("confirmPassword")}
161          onBlur={handleBlur("confirmPassword")}
162          placeholder="Confirmar contraseña"
163          secureTextEntry
164        />
165        {touched.confirmPassword && errors.confirmPassword && (
166          <Text style={styles.errorText}>{errors.confirmPassword}</Text>
167        )}
168      </View>
169
170      <TouchableOpacity style={styles.button} onPress={handleSubmit}>
171        <Text style={styles.buttonText}>Registrarse</Text>
172      </TouchableOpacity>
173    </View>
174  );
175}
176
177const styles = StyleSheet.create({
178  container: {
179    padding: 20,
180  },
181  inputContainer: {
182    marginBottom: 15,
183  },
184  input: {
185    height: 50,
186    backgroundColor: "white",
187    borderWidth: 1,
188    borderColor: "#ddd",
189    borderRadius: 8,
190    paddingHorizontal: 15,
191    fontSize: 16,
192  },
193  inputError: {
194    borderColor: "#ff3b30",
195  },
196  errorText: {
197    color: "#ff3b30",
198    fontSize: 12,
199    marginTop: 5,
200    marginLeft: 5,
201  },
202  button: {
203    backgroundColor: "#007AFF",
204    height: 50,
205    borderRadius: 8,
206    justifyContent: "center",
207    alignItems: "center",
208    marginTop: 10,
209  },
210  buttonText: {
211    color: "white",
212    fontSize: 18,
213    fontWeight: "600",
214  },
215});

Ejemplos de Código

3 ejemplos

Regex de email simple

typescript
1```typescript
2const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
3```

Estilo condicional de error

typescript
1```typescript
2style={[
3  styles.input,
4  touched.email && errors.email && styles.inputError,
5]}
6```

Validación en submit

typescript
1```typescript
2const validationErrors = validate();
3if (Object.keys(validationErrors).length > 0) {
4  setErrors(validationErrors);
5  return;
6}
7```

Recursos

5 recursos disponibles

¡Hora de Practicar!

PrácticaIntermedio15 min

Práctica

Practiquemos.

  • Crear un formulario de registro con name, email, password, confirmPassword.
  • Implementar touched para mostrar errores solo al perder el foco.
  • Agregar estilos de error a inputs inválidos.
  • Validar en handleSubmit y bloquear el envío si hay errores.

Desafío de Código

EjercicioIntermedio15 min

Ejercicios Prácticos

Realiza los ejercicios en el proyecto de la practica anterior

  1. Añade una regla para que la contraseña tenga al menos una mayúscula.
  2. Muestra un mensaje de error general si faltan campos obligatorios.
  3. Implementa validación de email con un regex alternativo más estricto.
  4. Resalta el primer campo con error haciendo focus automático.

Documentación Oficial

DocumentaciónPrincipiante0

Form Validation UX

Buenas prácticas para mostrar errores de validación.

Documentación Oficial

DocumentaciónPrincipiante0

React Native TextInput

Props relevantes para validación y feedback visual.

Documentación Oficial

DocumentaciónPrincipiante0

Accessibility in React Native

Recomendaciones para mensajes de error accesibles.

ALVESC ACADEMY - Plataforma Educativa