💻 Essa minha lib em react-native tem um truque em react muito legal!
Explicando como eu resolvi o problema do react-hook-form no react-native
Pra quem já tentou usar o react-hook-form com React Native sabe que é bem verboso, pois é preciso envolver o seu input pelo componente Controller e passar o control para ele, e é aí que a minha lib entra, conheça a REACT NATIVE FORMIFY, uma biblioteca poderoda de formulário que se integra perfeitamente com a flexibilidade do react-hook-form
Como é atualmente (Sem a lib) 🤔
import { Text, View, TextInput, Button, Alert } from "react-native"
import { useForm, Controller } from "react-hook-form"
export default function App() {
const {
control,
handleSubmit,
formState: { errors },
} = useForm({
defaultValues: {
firstName: "",
lastName: "",
},
})
const onSubmit = (data) => console.log(data)
return (
<View>
<Controller
control={control}
rules={{
required: true,
}}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
placeholder="First name"
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
name="firstName"
/>
{errors.firstName && <Text>This is required.</Text>}
<Controller
control={control}
rules={{
maxLength: 100,
}}
render={({ field: { onChange, onBlur, value } }) => (
<TextInput
placeholder="Last name"
onBlur={onBlur}
onChangeText={onChange}
value={value}
/>
)}
name="lastName"
/>
<Button title="Submit" onPress={handleSubmit(onSubmit)} />
</View>
)
}
Como é feito com a lib 😄
import { Button } from 'react-native'
import { useForm } from 'react-hook-form'
import Form from 'react-native-formify'
export default function App () {
const { control, handleSubmit, formState: { errros }} = useForm()
const onSubmit = (data) => {
// handle data
}
return (
<>
<Form>
<Form.Input name="name" label="Name" />
<Form.Input name="lastName" label="Last Name" />
</Form>
<Button onPress={handleSubmit(onSubmit)}>Submit</Button>
</>
)
}
Ela recebe várias props para estilização para você customizar bastante os componentes!
interface Erros {
[key: string]: {
message: string;
}
}
interface InputProps extends TextInputProps {
label?: string;
name: string;
onChange?: (...event: any[]) => void;
errors?: Erros;
_containerProps?: ViewProps
_errorTextProps?: TextProps
}
Esta lib já está no npm para você baixar! Mas afinal, como ela funciona? Qual o truque? Eu explico:
- Map de children components
- cloneElement() do React
Com isso em mente, vamos para a implementação
function Main<T>({
children,
control,
errors,
}: {
children: JSX.Element[] | JSX.Element;
control: Control<T extends FieldValues ? T : FieldValues, any>;
errors: FieldErrors<T extends FieldValues ? T : FieldValues>;
}) {
return (
<>
{Children.map(children, (child) => {
const isReactElement =
child &&
typeof child === "object" &&
"type" in child &&
"props" in child;
if (!isReactElement) return null;
return (
<Controller
control={control}
name={child.props.name}
render={({ field: { onChange, value } }) => {
return cloneElement(child, { onChange, value, errors });
}}
/>
);
})}
</>
);
}
Podem ver ali que meu componente Main recebce um array de elementos JSX
children: JSX.Element[] | JSX.Element;
Depois, mapeia sobre eles:
{Children.map(children, (child) => {
}}
Ao final do map, ele cria um elemento Controller para cada child, injetando as props requisitadas para o react-hook-form funcionar, fazendo isso com o cloneElement, onde o segundo parâmetro será as props a serem "injetadas" no componente
<Controller
control={control}
name={child.props.name}
render={({ field: { onChange, value } }) => {
return cloneElement(child, { onChange, value, errors });
}}
/>
Basicamente com isso o problema está resolvido, você pode passar diversos componentes de Input para ele e ele irá injetar essas props necessárias para o funcionamento.