import * as React from 'react'; import { isFunction } from '@vkontakte/vkjs'; import { useIsomorphicLayoutEffect } from '../lib/useIsomorphicLayoutEffect'; import { warnOnce } from '../lib/warnOnce'; export interface UseEnsuredControlProps> { value?: V | undefined; defaultValue: V; disabled?: boolean | undefined; onChange?: ((this: void, e: E) => any) | undefined; } export function useEnsuredControl>({ onChange: onChangeProp, disabled, ...props }: UseEnsuredControlProps): [V, (e: E) => any] { const [value, onChangeValue] = useCustomEnsuredControl(props); const onChange = React.useCallback( (e: E) => { if (disabled) { return; } onChangeValue(e.target.value); onChangeProp && onChangeProp(e); }, [onChangeValue, onChangeProp, disabled], ); return [value, onChange]; } export interface UseCustomEnsuredControlProps { value?: V | undefined; defaultValue: V; disabled?: boolean | undefined; onChange?: ((this: void, v: V) => any) | undefined; } const warn = warnOnce('useCustomEnsuredControl'); export function useCustomEnsuredControl({ value, defaultValue, disabled, onChange: onChangeProp, }: UseCustomEnsuredControlProps): [V, React.Dispatch>] { const isControlled = value !== undefined; const [localValue, setLocalValue] = React.useState(defaultValue); const preservedControlledValueRef = React.useRef(undefined); useIsomorphicLayoutEffect(() => { preservedControlledValueRef.current = value; }); const onChange = React.useCallback( (nextValue: React.SetStateAction) => { if (disabled) { return; } if (isFunction(nextValue)) { if (!isControlled) { setLocalValue((prevValue) => { const resolvedValue = nextValue(prevValue); if (onChangeProp) { onChangeProp(resolvedValue); } return resolvedValue; }); } else if (onChangeProp) { if (process.env.NODE_ENV === 'development') { if (preservedControlledValueRef.current === undefined) { warn( `A component is changing an uncontrolled input to be controlled. This is likely caused by the value changing from undefined to a defined value, which should not happen. Decide between using a controlled or uncontrolled input element for the lifetime of the component. More info: https://react.dev/link/controlled-components`, 'error', ); } } if (preservedControlledValueRef.current !== undefined) { const resolvedValue = nextValue(preservedControlledValueRef.current); onChangeProp(resolvedValue); } } } else { if (onChangeProp) { onChangeProp(nextValue); } if (!isControlled) { setLocalValue(nextValue); } } }, [disabled, isControlled, onChangeProp], ); return [isControlled ? value : localValue, onChange]; }