1

💻 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:

  1. Map de children components
  2. 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.

Você pode baixar essa lib agora mesmo! Ela está no

Alias, se você usa Native Base, eu também fiz uma lib para isso com os componentes deles, o qual se chama native-base-formify (é a que eu pessoalmente recomendo.)

Carregando publicação patrocinada...