Trying to use react-hook-form in combination with react-input mask
Asked Answered
F

7

9

I have the following setup. My mask will show up, but when I type in it it just skips to the end of the line I am not quite sure what I am doing wrong here. I have tried putting all the props in the parent component and passing them all with a spread, That didn't work. I can provide more debugging if someone can give me an idea on where to debugg first and I'll do it.

Thanks ahead of time

import React from "react"
import { useForm } from "react-hook-form"
import MaskedInput from "react-input-mask"

const Quote = () => {
  const { register, handleSubmit, watch, errors } = useForm();
  const [tel, setTel] = React.useState("");

  render(
    <MaskedInput
      mask="(780) 000-0000"
      alwaysShowMask
      onChange={(e) => setTel(e.target.value)}
      value={tel}
      name={data.title}
    >
      {(inputProps) => (
        <input
          ref={register({
            required: true,
            pattern: /^[\+]?[(]?[0-9]{3}[)]?[-\s\.]?[0-9]{3}[-\s\.]?[0-9]{4,6}$/im,
          })}
          value={inputProps.tel}
          name={inputProps.name}
          {...inputProps}
        />
      )}
    </MaskedInput>
  );
};
Fearsome answered 16/5, 2020 at 15:29 Comment(7)
I guess that both modules try to control the same input and conflict with each other.Gaullist
it's been done here, which is how I tried to figure it out github.com/react-hook-form/react-hook-form/blob/master/examples/…Fearsome
Here's there example working with the two packages codesandbox.io/s/trusting-agnesi-rdi5mFearsome
and when I do it like the example and put the component inside, it says it is not a function and can't be a child.Fearsome
omg it was my mask, it was not the right format. Now it worksFearsome
@AndersKitson yes I've seen that and tried it, but how to do with MatrialUI? With Controlled components? TextField?Xavier
Tried it: #66602428Xavier
F
2

Mask format was wrong needed to be in something like this

mask="(+7 (999) 999-99-99)"

Fearsome answered 16/5, 2020 at 16:0 Comment(0)
U
6

This solution work for me. You will need the following packages on your package.json:

"react-hook-form": "^7.34.0",
"@hookform/resolvers": "^2.9.7",
"react-input-mask": "^3.0.0-alpha.2",
"@types/react-input-mask": "^3.0.1",

You can install this version of react-input-mask with the comand ...

yarn add react-input-mask@next
yarn add @types/react-input-mask

Here is the code:

<InputMask
  // mask options
  mask={"99.999.999/9999-99"}
  alwaysShowMask={false}
  maskPlaceholder=''
  // input options
  type={'text'}
  placeholder="Ex: 00.000.000/0000-00"
  // react hook form register
  {...register("cnpj", { required: true })}
/>
Unmindful answered 6/8, 2022 at 20:47 Comment(0)
I
3

To help others

If you're using not controlled Input Fields (like native input), ou can use a function to mask the input

This cant be used with controlled input fields like (Material UI)

Example @component/input.tsx

import React from 'react'
import { Container, ErrorMessage } from './styles'

interface InputProps {
  label?: string | true
  defaultValue?: string
  name?: string
  type?: string
  mask?: (value: string) => string
  placeholder?: string
  disabled?: boolean
  error?: any
  value?: string
  register?: any
}
const Input: React.FC<InputProps> = ({
  label,
  defaultValue,
  name,
  type,
  mask = (value: string) => value,
  value,
  placeholder,
  disabled,
  error,
  register,
  ...inputProps
}) => {
  return (
    <Container>
      {label && (
        <label htmlFor={name}>
          {(typeof label === 'string' && label) ||
            placeholder ||
            'Preencha este campo'}
        </label>
      )}
      <input
        onChange={e => (e.target.value = `${mask(e.target.value)}`)}
        disabled={disabled}
        ref={register}
        id={name}
        name={name}
        type={type}
        value={value}
        placeholder={placeholder}
        defaultValue={defaultValue}
        {...inputProps}
      />
      {error && <ErrorMessage>{error.message}</ErrorMessage>}
    </Container>
  )
}

export default Input

Usage example @page/form.tsx

function CPFMask(v: string): string {
  v = v.replace(/\D/g, '')
  v = v.replace(/^(\d{3})(\d)/g, '$1.$2')
  v = v.replace(/^(\d{3})\.(\d{3})(\d)/, '$1.$2.$3')
  v = v.replace(/^(\d{3})\.(\d{3})\.(\d{3})(\d)/, '$1.$2.$3-$4')
  v = v.replace(/^(\d{3})\.(\d{3})\.(\d{3})\/(\d{2})(\d)/, '$1.$2.$3-$4')
  return v.substring(0, 14)
}

...

<Input
      type="text"
      mask={CPFMask}
      placeholder="CPF"
      name="cpf"
      label
      register={register({
        required: {
          value: true,
          message: 'CPF é obrigatório',
        },
        pattern: {
          value: /([0-9]{2}[\.]?[0-9]{3}[\.]?[0-9]{3}[\/]?[0-9]{4}[-]?[0-9]{2})|([0-9]{3}[\.]?[0-9]{3}[\.]?[0-9]{3}[-]?[0-9]{2})/i,
          message: 'CPF inválido',
        },
      })}
      error={errors.cpf}
    />
...
Impotence answered 3/3, 2021 at 18:22 Comment(1)
What about with Material UI? There is a CodeBin with it. #66602428Xavier
E
3

For those who are using react-hook-form version 7, here is an example how to get it to work:

    <Controller
      name="your input name"
      control={control}
      defaultValue=""
      rules={{
        required: true,
      }}
      render={({ field }) => (
        <MaskedInput
          mask="99/99"
          maskChar=""
          value={field.value}
          onChange={field.onChange}
        >
          {(inputProps: any) => (
            <input
              {...inputProps}
              type="text"
            />
          )}
        </MaskedInput>
      )}
    />
Ejectment answered 19/8, 2021 at 2:36 Comment(0)
F
2

Mask format was wrong needed to be in something like this

mask="(+7 (999) 999-99-99)"

Fearsome answered 16/5, 2020 at 16:0 Comment(0)
U
1

Here's an example, wrap the children with a function, let's think of InputMask as Controller and children as render. So I put the ref prop on the children to redirect the ref errors directly to the children or render, not the Controller.

<InputMask name="npwp" mask="99.999.999.9-999.999">
  {(inputProps) => (
    <input
      {...inputProps}
      ref={register}
      type="text"
      placeholder="Input NPWP"
    />
  )}
</InputMask>
Uppish answered 5/7, 2021 at 17:38 Comment(0)
W
0

This is how I did it without using register, and with a different masking library (s-yadav/react-number-format).

A work-around, as I could not get it to work with {...register()}.

<NumberFormat
    type="tel"
    defaultValue={formDefaultValue}
    onValueChange={(values) => {
        // Mirror the value for form validation
        setFormValue("amount", values.value);
    }}
    customInput={(props) => {
        return (
            <Input
                name="amount"
                formatProps={props}
                errors={formErrors}
            />
        );
    }}
/>

Inside Input component:

<input
    {...(formatProps ? formatProps : {})}
/>
Wegner answered 22/8, 2022 at 14:53 Comment(0)
I
0

Workaround with extended interface:

file: src/@types/react.d.ts

Content:

import { Props as BaseProps } from 'react-input-mask'

declare module 'react-input-mask' {
  interface Props extends BaseProps {
    children: (inputProps: any) => React.ReactNode
  }
}
Impertinence answered 2/2 at 1:27 Comment(1)
Your answer could be improved with additional supporting information. Please edit to add further details, such as citations or documentation, so that others can confirm that your answer is correct. You can find more information on how to write good answers in the help center.Wobbling

© 2022 - 2024 — McMap. All rights reserved.