react-hook-form with formatted numeric values

I have an "amount" field which is numeric in my react-hook-form. Since the amounts can grow up to the millions in my case, I want to format the displayed value having a thousand separator for better UX. I looked for libs for formatting these values, and the best (maintained, easy to use, high popularity) I could find was react-number-format, so I gave it a shot. I found some attempts, all telling I might need to make my input "uncontrolled". These are the 2 ways I tried: https://stackoverflow.com/a/69258165/3045531 https://stackoverflow.com/a/63091512/3045531 My problem is that the actual form value linked to the field in the form's context is not numeric but a string, since it's handling the formatted value there, not the numeric one. How can I make the form handle the numeric value instead of the formatted one, so my Zod validation won't fail there? I would prefer not to use coerce in the schema to keep it cleaner.
Stack Overflow
How do I integrate React hook form with NumberFormat (react-number-...
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'; i...
Stack Overflow
Thousand separator input with React Hooks
I would like to add on a input a thousand separator using React Hooks but I'm not sure how. I have tried the below code so far and is not working. Can you please point out what could be the issue a...
18 Replies
Brendonovich
Brendonovich•2y ago
If you do use react-number-format, you can use the onValueChange callback to get the floatValue and pass that to the onChange from Controller/useController
Froxx
FroxxOP•2y ago
Tried that. Still results in string values:
const NumberInput = (props) => {
const { name } = props;

const form = useFormContext();

// always logs string values
console.log(form.getValues(name));

return (
<Controller
{...{
control: form.control,
name,
render: ({ field: { value, onChange } }) => (
<NumericFormat
{...{ value, onChange }}
onValueChange={({ floatValue }) => {
form.setValue(name, floatValue);
}}
thousandSeparator=","
/>
),
}}
/>
);
};
const NumberInput = (props) => {
const { name } = props;

const form = useFormContext();

// always logs string values
console.log(form.getValues(name));

return (
<Controller
{...{
control: form.control,
name,
render: ({ field: { value, onChange } }) => (
<NumericFormat
{...{ value, onChange }}
onValueChange={({ floatValue }) => {
form.setValue(name, floatValue);
}}
thousandSeparator=","
/>
),
}}
/>
);
};
Brendonovich
Brendonovich•2y ago
Possibly because you're passing both onChange and onValueChange, try calling onChange within onValueChange instead of using setValue
Froxx
FroxxOP•2y ago
Oh. I tried that before, but also passed onChange to <NumericFormat /> as well just as I did with value. So it probably worked for a moment, but got overwritten instantly. After only passing value and calling onChange within onValueChange that works! Anyway there are still some more problems due to the input being controlled here: - It doesn't respond to a <button type="reset" /> - RHF doesn't auto focus the field after a failed validation - I'd like to hand in my standard input component as customInput to NumericFormat, but since that one is uncontrolled as well, it throws some issues Is there no way to do the formatting in an uncontrolled state?
Brendonovich
Brendonovich•2y ago
for point 3 i can say that customInput likely isn't designed for uncontrolled inputs, you'll have to make a custom one out of divs or something For 2 you need to pass field.ref to the component so that it can focus Point 3 i'm not quite sure since the value should get reset internally and propagated on rerender
Finn
Finn•2y ago
Brendon when he sees a post about forms todd
Brendonovich
Brendonovich•2y ago
yeah i'm kinda the form guy now
Froxx
FroxxOP•2y ago
Helped me a lot in my last question, so I appreciate it 😄
Brendonovich
Brendonovich•2y ago
👀
Finn
Finn•2y ago
😩😩😩
Brendonovich
Brendonovich•2y ago
make a stackblitz with a basic example and i'll take a look at point 3
Froxx
FroxxOP•2y ago
I tried that but can't figure out how to actually pass the ref there since it's a functional component, and React doesn't like that
Brendonovich
Brendonovich•2y ago
forwardRef assuming that's what u mean
Froxx
FroxxOP•2y ago
Yeah, I read that in the error message as well, but I don't know how React wants to get that there. I tried this, but it doesn't like it:
<Controller
{...{
control: form.control,
name,
render: ({ field: { value, onChange, ref } }) =>
// eslint-disable-next-line react/display-name
React.forwardRef(() => (
<NumericFormat
{...{ value, ref }}
onValueChange={({ floatValue }) => {
onChange(floatValue);
}}
thousandSeparator=","
<Controller
{...{
control: form.control,
name,
render: ({ field: { value, onChange, ref } }) =>
// eslint-disable-next-line react/display-name
React.forwardRef(() => (
<NumericFormat
{...{ value, ref }}
onValueChange={({ floatValue }) => {
onChange(floatValue);
}}
thousandSeparator=","
I don't really know a lot about refs until this point poohheh taking the 2nd parameter from forwardsRef instead of the one from Controller.render doesn't solve it
Brendonovich
Brendonovich•2y ago
try passing ref to getInputRef no need for forwardRef
Froxx
FroxxOP•2y ago
Ah. That makes sense Well I guess I could go for the controlled number inputs for now. I'm not too happy since the simple "add register(name) to it, and everything works fine in the context"-way of RHF is really handy. Anyway, I get my formatted value this way, my validation works, and those are the main points. Taking care of a working reset functionality etc can be seen as "sugar" for the future. So thanks again to you, @Brendonovich , lord of the forms ✨
Brendonovich
Brendonovich•2y ago
yeah for a use case like this you pretty much cannot use uncontrolled inputs, since with uncontrolled inputs the value is derived from the text. glad i could help! also i'd recommend using useController over Controller
Froxx
FroxxOP•2y ago
True. Hooks are nice. Thanks
Want results from more Discord servers?
Add your server