taiga-family / maskito

Collection of libraries to create an input mask which ensures that user types value according to predefined format.
https://maskito.dev
Apache License 2.0
1.43k stars 33 forks source link

πŸ“š - Improving Input Mask Behavior for React-Hook-Form Integration #1795

Closed NereonNeo closed 3 weeks ago

NereonNeo commented 3 weeks ago

What is the affected URL?

https://maskito.dev/frameworks/react

Description

I was creating an input mask component using react-hook-form, and I encountered an issue. πŸ˜… When I enabled the mask, react-hook-form stopped working. However, when the mask is disabled, react-hook-form functions correctly. This led me to investigate what was happening. πŸ˜„

The documentation suggested using the utility function withMaskitoRegister, but it wasn't suitable for my implementation since I was creating the mask within the component. I didn't want to pass the register function into the component and make it explicitly dependent on react-hook-form. πŸ™ƒ

From what I gathered, maskito takes control of the onChange event, which causes react-hook-form to also attempt to use it. As a result, I had to add the onChange event from react-hook-form to the onInput event, while I isolated the mask logic in onBeforeInput. Finally, everything is working! 😌 I hope I was able to explain everything clearly.

Ultimately, I would like to suggest adding a similar example to the documentation, as it would make things easier for everyone.

I'd also like to know if I'm using useMaskito correctly? πŸ˜…

Here’s the code:

interface IMaskInputProps extends React.InputHTMLAttributes { maskOption: MaskInputOptions; }

export const MaskInput = forwardRef<HTMLInputElement, IMaskInputProps>((props, ref) => {
  const { maskOption, onChange, ...otherProps } = props;

  const inputRef = useRef<HTMLInputElement | null>(null);
  const maskedMaskitoRef = useMaskito(InputMaskWithPattern[maskOption]);
  useImperativeHandle(ref, () => inputRef.current!, []);

  const handleOnBeforeInputChange = () => {
    maskedMaskitoRef(inputRef.current);
  };

  const handleOnInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    onChange?.(event);
  };

  return <Input {...otherProps} ref={inputRef} onBeforeInput={handleOnBeforeInputChange} onInput={handleOnInputChange} />;
});

Which browsers have you used?

Which operating systems have you used?

NereonNeo commented 3 weeks ago

Hello again everyone! My initial approach didn't work quite right with maskito plugins, so I had to adjust it to the current version. =)

interface IMaskInputProps extends React.InputHTMLAttributes<HTMLInputElement> {
  maskOption: MaskInputOptions;
}

export const MaskInput = forwardRef<HTMLInputElement, IMaskInputProps>((props, ref) => {
  const { maskOption, onChange, ...otherProps } = props;

  const inputRef = useRef<HTMLInputElement | null>(null);
  const maskedMaskitoRef = useMaskito(InputMaskWithPattern[maskOption]);

  useImperativeHandle(ref, () => inputRef.current!, []);

  const handleOnInputChange = (event: React.ChangeEvent<HTMLInputElement>) => {
    onChange?.(event);
  };

  return (
    <Input
      {...otherProps}
      ref={(node) => {
        inputRef.current = node;
        maskedMaskitoRef(node);
      }}
      onInput={handleOnInputChange}
    />
  );
});
nsbarsukov commented 3 weeks ago

@NereonNeo Hello!

As documentation says: "There is not silver bullet how to integrate Maskito with any library for form-building".

The main points you should keep in mind:

Everything else depends on the developer's task. I don't think we should overload the documentation with every possible case of third-party intergrations πŸ€”

NereonNeo commented 3 weeks ago

I think there’s logic in what you’re saying, so we can go ahead and dismiss my suggestion. =)

waterplea commented 2 weeks ago

Image