CaioQuirinoMedeiros / react-native-mask-input

🎭:iphone: A simple and effective Text Input with mask for ReactNative on iOS and Android. Includes obfuscation characters feature.
https://www.npmjs.com/package/react-native-mask-input
MIT License
304 stars 31 forks source link

Doesn't work with react-hook-form (RHF) Controller inputs #46

Open soaresgus opened 10 months ago

soaresgus commented 10 months ago

The library doesn't work with with react-hook-form (RHF) Controller inputs. I make a lot of tests and the best solution is use a default useState from React. That is my code with RHF:

Perhaps in a future i go up a pull request to fix this.

import React, { useEffect, useState } from 'react';
import { Image, ScrollView, Text, View } from 'react-native';
import {
  Button,
  PaperProvider,
  RadioButton,
  useTheme,
} from 'react-native-paper';

import { Input } from '../../components/Input';
import logoSrc from '../../../assets/logo.png';
import { StatusBar } from 'expo-status-bar';
import { BloodTypes } from '../../types/BloodTypes';
import { Radio } from '../../components/Radio';
import { zodResolver } from '@hookform/resolvers/zod';
import { Controller, useForm } from 'react-hook-form';
import { z } from 'zod';
import { Link } from '@react-navigation/native';
import { cep as cepPromise } from 'cep-promise';
import { Masks, useMaskedInputProps } from 'react-native-mask-input';

const SignIn = () => {
  const [isLoading, setIsLoading] = useState(false);

  const createUserFormSchema = z.object({
    email: z
      .string()
      .email('Formato de e-mail inválido')
      .min(1, 'Campo obrigatório'),
    password: z.string().min(6, 'A senha precisa de no mínimo 6 caracteres'),
    name: z.string().min(1, 'Campo obrigatório'),
    bloodType: z
      .enum(['A+', 'A-', 'B+', 'B-', 'AB+', 'AB-', 'O+', 'O-'])
      .default('A+'),
    userType: z.enum(['doador', 'necessita']).default('doador'),
    cep: z.string(),
  });

  const {
    control,
    handleSubmit,
    formState: { errors },
    getValues,
    setValue,
  } = useForm({
    resolver: zodResolver(createUserFormSchema),
    defaultValues: {
      name: '',
      password: '',
      email: '',
      bloodType: 'A+',
      userType: 'doador',
      cep: '',
    },
  });

  const appTheme = useTheme();

  const cepMask = [/\d/, /\d/, /\d/, /\d/, /\d/, '-', /\d/, /\d/, /\d/];
  const cepMaskedInputProps = useMaskedInputProps({
    value: getValues('cep'),
    onChangeText: (masked, unmasked, obfuscated) => setValue('cep', unmasked),
    mask: cepMask,
  });

  const createUser = (data: any) => {
    console.log(data);
  };

  return (
    <ScrollView
      contentContainerStyle={{
        width: '100%',
        paddingTop: 80,
        paddingBottom: 40,
        alignItems: 'center',
        justifyContent: 'center',
        backgroundColor: '#200c10',
      }}
    >
      <Image source={logoSrc} className="w-80 h-40" resizeMode="contain" />
      <View className="flex gap-4 px-8">
        <View>
          <Controller
            control={control}
            rules={{
              required: true,
            }}
            render={({ field: { onChange, onBlur, value } }) => (
              <Input onBlur={onBlur} label="CEP" {...cepMaskedInputProps} />
            )}
            name="cep"
          />
        </View>

        <Button
          className="w-80"
          mode="outlined"
          loading={isLoading}
          onPress={handleSubmit(createUser)}
        >
          {!isLoading && 'CRIAR'}
        </Button>
        <Link to={{ screen: 'Login' }}>
          <Button className="w-80" mode="outlined">
            VOLTAR
          </Button>
        </Link>
      </View>
      <StatusBar style="light" translucent backgroundColor="#200c10" />
    </ScrollView>
  );
};

export default SignIn;
darlanhms commented 10 months ago

You have to pass onChange and value props to your input

<Controller
  control={control}
  rules={{
    required: true,
  }}
  render={({ field: { onChange, onBlur, value } }) => (
    <Input onBlur={onBlur} onChange={onChange} value={value} label="CEP" {...cepMaskedInputProps} />
  )}
  name="cep"
 />
soaresgus commented 10 months ago

I forgot to write on GitHub code but I'm sure I typed

lourencorodrigo commented 9 months ago

@soaresgus I don't know if you get it or not, but you could use the useController from react-hooks-form.

const { control } = useForm<FormData>();

const {field: fieldPhoneNumber} = useController<FormData>({
  name: 'phoneNumber',
  control,
});

const maskedInputProps = useMaskedInputProps({
  value: fieldPhoneNumber.value,
  onChangeText: fieldPhoneNumber.onChange,
  mask: Masks.BRL_PHONE,
});
<Controller
  control={control}
  name="phoneNumber"
  render={({field, fieldState}) => (
    <TextInput
      {...maskedInputProps}
    />
  )}
/>