@carbon/react
Version:
React components for the Carbon Design System
59 lines (53 loc) • 2.5 kB
JavaScript
/**
* 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 };