Search code examples
reactjsreact-hook-formreact-number-format

How do I integrate React hook form with NumberFormat (react-number-format)?


I am trying to use React hook form with NumberFormat without Controller and without ReactDOM.findDOMNode (which is deprecated and discouraged). The following code works

import React from 'react';
import ReactDOM from 'react-dom';
import { useForm } from 'react-hook-form';
import NumberFormat from 'react-number-format';

function FormWorking() {
  const { register, handleSubmit, getValues } = useForm();

  function onSubmit() {
    console.log(getValues());
  }

  const cardNumberReg = register('cardNumber');
  
  return (
    <form onSubmit={handleSubmit(onSubmit)}>
      <NumberFormat
          format="#### #### #### ####"
          {...cardNumberReg}
          ref={(inst) => (cardNumberReg.ref(ReactDOM.findDOMNode(inst)))}
      />
    </form>
  );
}

ReactDOM.render(
  <FormWorking />, document.getElementById('root')
);

but I don't want to use findDOMNode. None of the below attempts is working (cardNumber is blank or undefined in the log)

      <NumberFormat
          format="#### #### #### ####"
          {...cardNumberReg}
          getInputRef={(e: any) => (cardNumberReg.ref(e))}
      />

      <NumberFormat
          format="#### #### #### ####"
          {...cardNumberReg}
          ref={cardNumberReg.ref}
      />

      <NumberFormat
          format="#### #### #### ####"
          {...cardNumberReg}
          ref={(e) => cardNumberReg.ref(e)}
      />

I am new to react and any help would be greatly appreciated.


Solution

  • My take on this is to use the Controller component from react hook form, in that case you are handling the NumberFormat as a controlled component, you need to do something like this:

    import { useForm, Controller } from "react-hook-form";
    import NumberFormat from "react-number-format";
    
    export default function App() {
      const {
        control,
        handleSubmit,
        formState: { errors }
      } = useForm();
    
      const onSubmit = (data) => console.log(data);
    
      return (
        <form onSubmit={handleSubmit(onSubmit)}>
          <Controller
            control={control}
            name="cardNumber"
            render={({ field: { onChange, name, value } }) => (
              <NumberFormat
                format="#### #### #### ####"
                name={name}
                value={value}
                onChange={onChange}
              />
            )}
          />
          <input type="submit" />
        </form>
      );
    }
    

    Basically you need to pass the control value coming from the useForm() function and give the Controller a name, in your case is cardNumber, after that you will use a render function in the Controller, where your NumberFormat controller will be used, notice that the render prop is a function that gives you all the necessary values in order to make your input work, like the onChange, the value and the name. You can take a look at the docs to see all the available props.

    Keep in mind that normally you want to use the register function for every input, but there are cases with UI libraries that using the Controller to make the input controlled is the best idea

    Also here's a sandbox with a working example.