( props: UseControlledProps<T>, )
| 23 | } |
| 24 | |
| 25 | export default function useControlled<T = unknown>( |
| 26 | props: UseControlledProps<T>, |
| 27 | ): [T, React.Dispatch<React.SetStateAction<T | undefined>>] { |
| 28 | const { controlled, default: defaultProp, name, state = 'value' } = props; |
| 29 | // isControlled is ignored in the hook dependency lists as it should never change. |
| 30 | const { current: isControlled } = React.useRef(controlled !== undefined); |
| 31 | const [valueState, setValue] = React.useState<T | undefined>(defaultProp); |
| 32 | const value = isControlled ? controlled : valueState; |
| 33 | |
| 34 | if (process.env.NODE_ENV !== 'production') { |
| 35 | React.useEffect(() => { |
| 36 | if (isControlled !== (controlled !== undefined)) { |
| 37 | console.error( |
| 38 | [ |
| 39 | `MUI: A component is changing the ${ |
| 40 | isControlled ? '' : 'un' |
| 41 | }controlled ${state} state of ${name} to be ${isControlled ? 'un' : ''}controlled.`, |
| 42 | 'Elements should not switch from uncontrolled to controlled (or vice versa).', |
| 43 | `Decide between using a controlled or uncontrolled ${name} ` + |
| 44 | 'element for the lifetime of the component.', |
| 45 | "The nature of the state is determined during the first render. It's considered controlled if the value is not `undefined`.", |
| 46 | 'More info: https://fb.me/react-controlled-components', |
| 47 | ].join('\n'), |
| 48 | ); |
| 49 | } |
| 50 | }, [state, name, controlled]); |
| 51 | |
| 52 | const { current: defaultValue } = React.useRef(defaultProp); |
| 53 | |
| 54 | React.useEffect(() => { |
| 55 | if (!isControlled && JSON.stringify(defaultProp) !== JSON.stringify(defaultValue)) { |
| 56 | console.error( |
| 57 | [ |
| 58 | `MUI: A component is changing the default ${state} state of an uncontrolled ${name} after being initialized. ` + |
| 59 | `To suppress this warning opt to use a controlled ${name}.`, |
| 60 | ].join('\n'), |
| 61 | ); |
| 62 | } |
| 63 | }, [JSON.stringify(defaultProp)]); |
| 64 | } |
| 65 | |
| 66 | const setValueIfUncontrolled: React.Dispatch<React.SetStateAction<T | undefined>> = |
| 67 | React.useCallback((newValue: React.SetStateAction<T | undefined>) => { |
| 68 | if (!isControlled) { |
| 69 | setValue(newValue); |
| 70 | } |
| 71 | }, []); |
| 72 | |
| 73 | // TODO: provide overloads for the useControlled function to account for the case where either |
| 74 | // controlled or default is not undefined. |
| 75 | // In that case the return type should be [T, React.Dispatch<React.SetStateAction<T>>] |
| 76 | // otherwise it should be [T | undefined, React.Dispatch<React.SetStateAction<T | undefined>>] |
| 77 | return [value as T, setValueIfUncontrolled]; |
| 78 | } |
searching dependent graphs…