Closed ilterugur closed 1 month ago
Hey there!
Since ref
prop is a callback, you can do something like this:
<YourAmazingInput
ref={node => {
maskitoRefCallback(node)
myInputRef.current = node
}}
/>
Hey guys,
Is there some way to display default value with mask applied when using react-hook-form?
I'm trying to use Maskito with currency mask and react-hook-form for manage my form state, but initial state is the raw number, and the input is only numbers without $ or . ,
Example: my input has initial value 12000 and it really display 12000, but only when start typing that mask are applied
Hey guys,
Is there some way to display default value with mask applied when using react-hook-form?
I'm trying to use Maskito with currency mask and react-hook-form for manage my form state, but initial state is the raw number, and the input is only numbers without $ or . ,
Example: my input has initial value 12000 and it really display 12000, but only when start typing that mask are applied
@mauriciocrecencio
Worked like a charm
const maskitoOptions: MaskitoOptions = {
mask: /^\d+$/,
}
function Input({onChange, ...props}: React.ComponentProps<'input'>){
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
event.currentTarget.value = maskitoTransform(
event.currentTarget.value,
maskitoOptions,
)
onChange?.(event)
}
return <input onChange={handleChange} {...props} />
}
Faced the refs conflict issue (such a delicate case for react XD ), because the useMaskito
hooks returns ref. And using react-hook-form
means accepting the ref as well for acceptable form state management.
Here is the thing.
I have the form input field (shadcn-ui form layer is being used, but does not matter, taking into account that we use react-hook-form
):
<FormField
control={form.control}
name="purchasePrice"
render={({ field }) => (
<FormItem className="col-span-2">
<div className="inline-flex items-center gap-1">
<FormLabel>Purchase Price</FormLabel>
<HelpTooltip Icon={CircleAlertIcon}>
// ...
</HelpTooltip>
</div>
<FormControl>
<CurrencyInput
placeholder="e.g $ 1.650"
value={field.value}
onChange={(event) => {
// react-hook-form schema requires number here, so I am parsing it and convert to numeric value
field.onChange(parseInt(event.target.value) || 0);
}}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
The <CurrencyInput/>
components has such code:
const CurrencyInput: React.ForwardRefRenderFunction<HTMLInputElement, React.ComponentPropsWithoutRef<'input'>> = (
props,
// Forwarded ref here
ref
) => {
// gnerated ref here
const currencyInputRef = useMaskito({
options: CURRENCY_INPUT_MASKITO_OPTIONS,
});
return (
<Input
ref={
// What should i pass here with my 2 refs, and both are important?
}
{...props}
/>
);
};
merge-refs
library did not work, because it is bad with accepring forward refs
<Input ref={mergeRefs(ref, currencyInputRef) as React.Ref<React.ElementRef<'input'>>} {...props} />;
So it is impossible to use the library in this way. WOuld it be possible to return not the ref as an option, but the props slice for this?
Man, if only react had directives...
@mykola-kolomoyets hey! Did you try solution that I provided above? Did it not work for some reason? In your case it would look something like this:
ref={node => {
currencyInputRef(node)
ref.current = node
}}
Hi, I'm trying to accomplish something similar but using the Controller implementation of react-hook-form with Maskito and Material UI.
I have the mask working when I am interacting with the field, but as soon as I tab away to the next field, the mask disappears, and the value returns to 0. It also does not appear when the form is first loaded. Any ideas?
Here is my implementation:
import { TextField } from "@mui/material";
import { Controller, RegisterOptions } from "react-hook-form";
import { MaskitoOptions } from "@maskito/core";
import { useMaskito } from "@maskito/react";
type InputTextProps = {
name: string;
label: string;
control: any;
mask: MaskitoOptions;
options?: RegisterOptions;
value?: string | number;
required?: boolean;
type?: "text"
};
export const FormMaskedInputText = ({
name,
label,
control,
mask,
options,
required,
}: InputTextProps): JSX.Element => {
const maskitoRef = useMaskito({ options: mask });
return (
<Controller
name={name}
control={control}
rules={options}
render={({
field: { onChange, onBlur, ref, value, name },
fieldState: { error },
}) => (
<TextField
helperText={error ? error.message : null}
error={!!error}
name={name}
onChange={onChange}
onBlur={onBlur}
inputRef={(el: HTMLElement | null) => {
maskitoRef(el);
ref(el);
}}
label={label}
required={required || true}
value={value}
/>
)}
/>
);
};
And then rendering with:
import { SubmitHandler, useForm } from "react-hook-form";
import Box from "@mui/material/Box";
import Grid2 from "@mui/material/Grid2";
import { maskitoNumberOptionsGenerator } from "@maskito/kit";
import { FormMaskedInputText } from "./FormMaskedInputText";
const type Demo = {
latitude: number;
longitude: number;
}
export const DemoForm = ({
data,
onSubmit,
}: {
data: Demo;
onSubmit: SubmitHandler<Demo>;
}): JSX.Element => {
const form = useForm<Demo>({
defaultValues: data,
});
const latitudeMask = maskitoNumberOptionsGenerator({
precision: 4,
decimalZeroPadding: true,
min: -90,
max: 90,
});
const longitudeMask = maskitoNumberOptionsGenerator({
precision: 4,
decimalZeroPadding: true,
min: -180,
max: 180,
});
return (
<Box component="form" onSubmit={form.handleSubmit(onSubmit)}>
<Grid2 container>
<Grid2>
<FormMaskedInputText
name="latitude"
label="Latitude"
control={form.control}
mask={latitudeMask}
/>
</Grid2>
<Grid2>
<FormMaskedInputText
name="longitude"
label="Longitude"
control={form.control}
mask={longitudeMask}
/>
</Grid2>
<input type="submit" />
</Grid2>
</Box>
);
};
@nextZed Hey - did you have any ideas on the use case I shared? Thanks!
@leverage-analytics could you please provide a stackblitz with example above?
@nextZed sure! Here is a demo!. Let me know if you have any questions. I appreciate your help!
I'm also facing this issue, did anyone made any progress?
Should we add a big ass warning at the top of React page to use proper onInput
in 2k24 if using the onChange
is still so prominent in React community?
@nextZed that's great! Thank you so much for your help.
Just one quick follow-up question. Based on the discussion above, should the implementation we're discussing now mask the default form values? That's the only part that's still not working as desired for me.
No, it only masks the value as user edits it by default. You can use maskitoTransform
helper function or a maskitoInitialCalibrationPlugin
:
https://maskito.dev/core-concepts/plugins#initial-calibration
The reason we have it like that is imagine the following scenario: A user is applying for a loan and enters a date within the limits of the form. Then they save it as a draft and open it in a few days. The limits would be different and the date user entered might fall out of them. If we automatically change it to the first available date (which min/max params do in date mask as you type it) — we would have altered the users input without them noticing. That could be very bad if the data is sensitive. So by default we do not change any data unless triggered by user input, but it's easy to enable by adding the plugin provided out of the box.
What is the affected URL?
No response
Description
Hello, first of all, thanks for the library. If I want to use use this library's react adapter with react-hook-form, what should I do? They both utilize ref props and we can give only one ref value to inputs. Or if I want to use a regular ref object on an input and also use maskito on that, how can I pass my own ref value while using maskito's ref callback value? Only way I found is giving maskito ref to a parent element and using elementPredicate option on useMaskito hook but it doesn't feel like it's the right way
Which browsers have you used?
Which operating systems have you used?