UNPKG

@carbon/react

Version:

React components for the Carbon Design System

59 lines (53 loc) 2.5 kB
/** * Copyright IBM Corp. 2016, 2023 * * This source code is licensed under the Apache-2.0 license found in the * LICENSE file in the root directory of this source tree. */ import { useState, useRef, useEffect } from 'react'; import { warning } from './warning.js'; /** * This hook simplifies the behavior of a component that has state which can be * both controlled and uncontrolled. It works like `useState`. You can use the * `onChange` callback to communicate state updates to parent components. * * Note: This hook will warn if the component switches between controlled and * uncontrolled states. */ const useControllableState = ({ defaultValue, name = 'custom', onChange, value }) => { const [state, internalSetState] = useState(typeof value !== 'undefined' ? value : defaultValue); const controlled = useRef(null); if (controlled.current === null) { controlled.current = typeof value !== 'undefined'; } const setState = stateOrUpdater => { const newValue = typeof stateOrUpdater === 'function' ? stateOrUpdater(state) : stateOrUpdater; if (controlled.current === false) { internalSetState(newValue); } if (onChange) { onChange(newValue); } }; useEffect(() => { const controlledValue = typeof value !== 'undefined'; // Uncontrolled -> Controlled if (controlled.current === false && controlledValue) { process.env.NODE_ENV !== "production" ? warning(false, `A component is changing an uncontrolled ${name} 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') : void 0; } // Controlled -> Uncontrolled if (controlled.current === true && !controlledValue) { process.env.NODE_ENV !== "production" ? warning(false, `A component is changing a controlled ${name} 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') : void 0; } }, [name, value]); if (controlled.current === true) { return [value, setState, controlled.current]; } return [state, setState, controlled.current]; }; export { useControllableState };