UNPKG

@mskcc/carbon-react

Version:

Carbon react components for the MSKCC DSM

72 lines (66 loc) 3.18 kB
/** * MSKCC 2021, 2024 */ import { useState, useRef, useEffect } from 'react'; import { warning } from './warning.js'; /** * This custom hook simplifies the behavior of a component if it has state that * can be both controlled and uncontrolled. It functions identical to a * useState() hook and provides [state, setState] for you to use. You can use * the `onChange` argument to allow updates to the `state` to be communicated to * owners of controlled components. * * Note: this hook will warn if a component is switching from controlled to * uncontrolled, or vice-versa. * * @param {object} config * @param {string} [config.name] - the name of the custom component * @param {any} config.defaultValue - the default value used for the state. This will be * the fallback value used if `value` is not defined. * @param {Function|undefined} config.onChange - an optional function that is called when * the value of the state changes. This is useful for communicating to parents of * controlled components that the value is requesting to be changed. * @param {any} config.value - a controlled value. Omitting this means that the state is * uncontrolled * @returns {[any, (v: any) => void]} */ function useControllableState(_ref) { let { defaultValue, name = 'custom', onChange, value } = _ref; const [state, internalSetState] = useState(value ?? defaultValue); const controlled = useRef(null); if (controlled.current === null) { controlled.current = value !== undefined; } function setState(stateOrUpdater) { const value = typeof stateOrUpdater === 'function' ? stateOrUpdater(state) : stateOrUpdater; if (controlled.current === false) { internalSetState(value); } if (onChange) { onChange(value); } } useEffect(() => { const controlledValue = value !== undefined; // Uncontrolled -> Controlled // If the component prop is uncontrolled, the prop value should be undefined if (controlled.current === false && controlledValue) { process.env.NODE_ENV !== "production" ? warning(false, 'A component is changing an uncontrolled %s component to be controlled. ' + 'This is likely caused by the value changing to a defined value ' + 'from undefined. Decide between using a controlled or uncontrolled ' + 'value for the lifetime of the component. ' + 'More info: https://reactjs.org/link/controlled-components', name) : void 0; } // Controlled -> Uncontrolled // If the component prop is controlled, the prop value should be defined if (controlled.current === true && !controlledValue) { process.env.NODE_ENV !== "production" ? warning(false, 'A component is changing a controlled %s component to be uncontrolled. ' + 'This is likely caused by the value changing to an undefined value ' + 'from a defined one. Decide between using a controlled or ' + 'uncontrolled value for the lifetime of the component. ' + 'More info: https://reactjs.org/link/controlled-components', name) : void 0; } }, [name, value]); if (controlled.current === true) { return [value, setState]; } return [state, setState]; } export { useControllableState };