viclafouch / mui-tel-input

📌 A phone number input designed for MUI (Material ui) V6 built with libphonenumber-js
https://viclafouch.github.io/mui-tel-input
MIT License
158 stars 65 forks source link

Implementation - Uncontrolled component #133

Closed remifongaufier closed 2 months ago

remifongaufier commented 6 months ago

Hello,

I would like to understand why the component can only be utilized as a "controlled component." I have a sense that the component could function as an "uncontrolled component" by implementing the props "defaultValue," adjusting the onChange function to be compliant with the type expected by the function onChange of Mui TextField, and ensuring that the input does not rely on the props value to update its state. This adjustment could potentially enhance user experience.

Due to the current implementation of the onChange function, it is not compatible with the function register from React-hook-form or the handleChange function from Formik.

Furthermore, when implementing MuiTelInput in a large form without using React-hook-form, it required to implement the following workaround to prevent unnecessary re-renders:

import { ChangeEvent, forwardRef, useState } from 'react';
import { MuiTelInput, MuiTelInputProps } from 'mui-tel-input';

type PhoneInputProps = Omit<MuiTelInputProps, 'onChange' | 'value' > & {
  defaultValue?: string;
};

const PhoneInput = forwardRef<HTMLDivElement, PhoneInputProps>(
  ({ defaultValue = '', ...props }, ref) => {
    const [phone, setPhone] = useState(defaultValue);

    const handleChange = useCallback((newPhone: string) => {
      setPhone(newPhone);
    }, []);

    return (
      <MuiTelInput
        {...props}
        onChange={handleChange}
        value={phone}
        ref={ref}
      />
    );
  }
);

PhoneInput.displayName = 'PhoneInput';
export default PhoneInput;

Thank you for your response and for all the effort you've put into developing mui-tel-input and mui-otp-input. They have been incredibly helpful and have saved me a significant amount of time.

If I can be of assistance in any way, please don't hesitate to let me know.

viclafouch commented 6 months ago

Hello @remifongaufier !

The component has multiple features that can't be compatible with an "uncontrolled" feature.

Like, if we enable forceCallingCode, the input value will be without the extension.

Due to the current implementation of the onChange function, it is not compatible with the function register from React-hook-form or the handleChange function from Formik.

That's why these libraries provide other methods to integrate with uncontrolled component :

https://react-hook-form.com/get-started#IntegratingControlledInputs

This library embraces uncontrolled components and native HTML inputs. However, it's hard to avoid working with external controlled components such as React-Select, AntD and MUI. To make this simple, we provide a wrapper component, Controller, to streamline the integration process while still giving you the freedom to use a custom register.

You don't have to only use the register function.

About your example, I don't really understand why you create a local state here. Providing a state at a parent level will be more usefull for your logic.

import { ChangeEvent, forwardRef, useState } from 'react';
import { MuiTelInput, MuiTelInputProps } from 'mui-tel-input';

type PhoneInputProps = MuiTelInputProps

const PhoneInput = forwardRef<HTMLDivElement, PhoneInputProps>(
  (props, ref) => {

    return (
      <MuiTelInput
        {...props}
        ref={ref}
      />
    );
  }
);

export default PhoneInput;

At this time, here, you don't even need this wrapper. If you can provide more details to explain why you need all your logic (with a useless useCallback), I can help you !

Cheers !