You can use a controlled input instead and wrap it in react-hook-form's Controller.
First, create your usual controlled input, which takes at least 2 props, value and onChange, and notifies the parent about changes:
onst NumberInput = React.forwardRef<HTMLInputElement, NumberInputProps>(({
onChange,
...inputProps
}, ref) => {
const handleChange = useCallback((e) => {
const { value } = e.target;
const parsedValue = parseInt(value, 10);
// If the value is not a number, we don't want to clear the input. Instead,
// just use the original string value and handle validation outside the input.
// You can be more strict about this by passing 0 or '' instead and changing
// the type attribute below to "number".
onChange(value === `${ parsedValue }` ? parsedValue : value);
}, [onChange]);
return (
<input
ref={ ref }
type="text"
onChange={ handleChange }
{ ...inputProps } />
);
});
To use this component with react-hook-form, simply wrap it in a Controller component, which will pass onChange, onBlur, value and ref to your component:
import ReactDOM from "react-dom";
import React, { useCallback } from "react";
import { useForm, Controller } from "react-hook-form";
const App: React.FC = () => {
const { handleSubmit, control, watch } = useForm();
return (
<form onSubmit={handleSubmit((data) => console.log(data))}>
<Controller
as={NumberInput}
control={control}
name="number"
defaultValue=""
/>
<pre>{ JSON.stringify(watch(), null, " ") }</pre>
<button type="submit">Submit</button>
</form>
);
};
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);