@carbon/react
Version:
React components for the Carbon Design System
52 lines (50 loc) • 2.43 kB
JavaScript
/**
* Copyright IBM Corp. 2016, 2026
*
* 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 { warning } from "./warning.js";
import { useEffect, useRef, useState } from "react";
//#region src/internal/useControllableState.ts
/**
* Copyright IBM Corp. 2016, 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* 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";
if (controlled.current === false && controlledValue) 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`);
if (controlled.current === true && !controlledValue) 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`);
}, [name, value]);
if (controlled.current === true) return [
value,
setState,
controlled.current
];
return [
state,
setState,
controlled.current
];
};
//#endregion
export { useControllableState };