En este artículo, vamos a mostrar cómo trabajar con formularios en React Native utilizando Typescript. A lo largo del artículo, desarrollaremos los componentes, hooks y demás necesarios para implementar un formulario completo y funcional. ¡Empecemos!
Antes de empezar, es importante tener en cuenta que este artículo asume que ya tienes conocimientos básicos de React Native y Typescript.
Componentes de formulario
Empecemos con los componentes de formulario. Estos son los componentes básicos que utilizaremos para construir nuestro formulario. Creamos un archivo FormInput.tsx en el que definimos el componente FormInput:
import React from 'react';
import { TextInput, StyleSheet } from 'react-native';
interface FormInputProps {
value: string;
placeholder: string;
onChangeText: (value: string) => void;
}
const FormInput: React.FC<FormInputProps> = ({ value, placeholder, onChangeText }) => {
return (
<TextInput
style={styles.input}
value={value}
placeholder={placeholder}
onChangeText={onChangeText}
/>
);
};
const styles = StyleSheet.create({
input: {
height: 40,
borderColor: 'gray',
borderWidth: 1,
marginBottom: 10,
paddingHorizontal: 10,
},
});
export default FormInput;
Este componente es una envoltura para el componente TextInput de React Native. Se espera que se le proporcione un value, un placeholder y una función onChangeText, que se ejecutará cada vez que el texto del TextInput cambie. El componente de estilo se utiliza para darle un aspecto adecuado.
Ahora, crearemos un archivo FormButton.tsx que definirá el componente FormButton:
import React from 'react';
import { TouchableOpacity, Text, StyleSheet } from 'react-native';
interface FormButtonProps {
label: string;
onPress: () => void;
}
const FormButton: React.FC<FormButtonProps> = ({ label, onPress }) => {
return (
<TouchableOpacity style={styles.button} onPress={onPress}>
<Text style={styles.label}>{label}</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
button: {
backgroundColor: 'blue',
paddingVertical: 10,
paddingHorizontal: 20,
borderRadius: 5,
},
label: {
color: 'white',
fontWeight: 'bold',
},
});
export default FormButton;
Este componente es una envoltura para el componente TouchableOpacity de React Native. Se espera que se le proporcione un label y una función onPress que se ejecutará cuando se presione el botón.
Podemos también incluir checkbox en el formulario, para eso vamos crear un nuevo componente llamado FormCheckbox:
import React from 'react';
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
interface FormCheckboxProps {
label: string;
value: boolean;
onValueChange: (newValue: boolean) => void;
}
const FormCheckbox: React.FC<FormCheckboxProps> = ({
label,
value,
onValueChange,
}) => {
const handlePress = () => {
onValueChange(!value);
};
return (
<TouchableOpacity style={styles.container} onPress={handlePress}>
<View style={styles.checkbox}>
{value && <View style={styles.checked} />}
</View>
<Text>{label}</Text>
</TouchableOpacity>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
checkbox: {
width: 20,
height: 20,
borderRadius: 4,
borderWidth: 1,
borderColor: '#ccc',
marginRight: 10,
alignItems: 'center',
justifyContent: 'center',
},
checked: {
width: 12,
height: 12,
borderRadius: 2,
backgroundColor: '#333',
},
});
export default FormCheckbox;
Este componente renderiza un checkbox con una etiqueta. El checkbox cambia de estado cuando se toca y llama a la función onValueChange proporcionada con el nuevo valor.
Para incluir un campo de contraseña en el formulario, podemos crear un nuevo componente llamado FormPasswordInput y usarlo en nuestro componente Form. Aquí mostramos un ejemplo:
import React, { useState } from 'react';
import { View, Text, TextInput, StyleSheet } from 'react-native';
interface FormPasswordInputProps {
value: string;
placeholder: string;
onChangeText: (value: string) => void;
}
const FormPasswordInput: React.FC<FormPasswordInputProps> = ({
value,
placeholder,
onChangeText,
}) => {
const [showPassword, setShowPassword] = useState(false);
const toggleShowPassword = () => {
setShowPassword(!showPassword);
};
return (
<View style={styles.container}>
<TextInput
style={styles.input}
value={value}
placeholder={placeholder}
secureTextEntry={!showPassword}
onChangeText={onChangeText}
/>
<Text style={styles.toggle} onPress={toggleShowPassword}>
{showPassword ? 'Ocultar' : 'Mostrar'}
</Text>
</View>
);
};
const styles = StyleSheet.create({
container: {
flexDirection: 'row',
alignItems: 'center',
},
input: {
flex: 1,
height: 40,
borderColor: '#ccc',
borderWidth: 1,
borderRadius: 4,
paddingHorizontal: 10,
},
toggle: {
marginLeft: 10,
color: '#666',
},
});
export default FormPasswordInput;
Este componente renderiza un campo de contraseña que puede ocultarse o mostrarse presionando un botón. El valor del campo se almacena en el estado local del componente y se pasa a la función onChangeText proporcionada cuando el usuario escribe en el campo.
Hooks de formulario
Ahora que hemos definido los componentes básicos, podemos empezar a construir los hooks que utilizaremos para manejar el estado del formulario. Crearemos un archivo useForm.ts en el que definiremos el hook useForm:
import { useState } from 'react';
interface FormFields {
[fieldName: string]: string;
}
interface FormErrors {
[fieldName: string]: string;
}
interface UseFormReturn {
fields: FormFields;
errors: FormErrors;
setFieldValue: (fieldName: string, value: string) => void;
handleSubmit: () => void;
}
const useForm = (onSubmit: (fields: FormFields) => void): UseFormReturn => {
const [fields, setFields] = useState<FormFields>({});
const [errors, setErrors] = useState<FormErrors>({});
const setFieldValue = (fieldName: string, value: string) => {
setFields((prevFields) => ({
...prevFields,
[fieldName]: value,
}));
};
const handleSubmit = () => {
const formErrors: FormErrors = {};
// Aquí realizamos las validaciones de cada campo
// Si un campo es inválido, lo agregamos al objeto `formErrors`
setErrors(formErrors);
if (Object.keys(formErrors).length === 0) {
onSubmit(fields);
}
};
return {
fields,
errors,
setFieldValue,
handleSubmit,
};
};
export default useForm;
Este hook devuelve cuatro propiedades:
El hook utiliza el estado de React para mantener el estado actual del formulario. También se encarga de realizar las validaciones de los campos del formulario y de llamar a la función onSubmit proporcionada cuando el formulario es válido.
Componente de formulario completo
Ahora que hemos definido los componentes básicos y los hooks necesarios, podemos crear nuestro componente de formulario completo. Crearemos un archivo Form.tsx en el que definiremos el componente Form:
import React from 'react';
import { View, StyleSheet, Text } from 'react-native';
import FormButton from './FormButton';
import FormCheckbox from './FormCheckbox';
import FormInput from './FormInput';
import FormPasswordInput from './FormPasswordInput';
import useForm from './useForm';
interface FormProps {
onSubmit: (fields: { [fieldName: string]: string | boolean | Date }) => void;
}
const Form: React.FC<FormProps> = ({ onSubmit }) => {
const { fields, errors, setFieldValue, handleSubmit } = useForm(onSubmit);
return (
<View style={styles.container}>
<FormInput
value={fields.name || ''}
placeholder="Nombre"
onChangeText={(value) => setFieldValue('name', value)}
/>
{errors.name && <Text style={styles.error}>{errors.name}</Text>}
<FormInput
value={fields.email || ''}
placeholder="Correo electrónico"
onChangeText={(value) => setFieldValue('email', value)}
/>
{errors.email && <Text style={styles.error}>{errors.email}</Text>}
<FormPasswordInput
value={fields.password || ''}
placeholder="Contraseña"
onChangeText={(value) => setFieldValue('password', value)}
/>
{errors.password && <Text style={styles.error}>{errors.password}</Text>}
<FormCheckbox
label="Acepto los términos y condiciones"
value={fields.acceptTerms || false}
onValueChange={(value) => setFieldValue('acceptTerms', value)}
/>
{errors.acceptTerms && (
<Text style={styles.error}>{errors.acceptTerms}</Text>
)}
<FormButton label="Enviar" onPress={handleSubmit} />
</View>
);
};
const styles = StyleSheet.create({
container: {
paddingHorizontal: 10,
},
error: {
color: 'red',
},
});
export default Form;
Este componente utiliza los componentes FormInput, FormButton, FormCheckbox,... que definimos anteriormente, así como el hook useForm para manejar el estado del formulario. El componente renderiza dos campos de entrada de texto para el nombre y el correo electrónico, otro para la contraseña y un botón de envío. También renderiza cualquier error que pueda haber ocurrido en los campos del formulario.