@utilityjs/use-controlled-prop
v2.0.0
Published
A React hook that handles controllable props.
Maintainers
Readme
UtilityJS | useControlledProp
A React hook that handles controllable props.
Features
- Controlled/Uncontrolled Pattern: Seamlessly supports both controlled and uncontrolled component patterns
- Type Safety: Full TypeScript support with generic types
- Development Warnings: Helpful warnings in development mode for common mistakes
- Flexible Fallbacks: Support for default values and fallback values
- Stable API: Consistent setter function that only works in uncontrolled mode
Installation
npm install @utilityjs/use-controlled-propor
pnpm add @utilityjs/use-controlled-propUsage
Basic Example
import { useControlledProp } from "@utilityjs/use-controlled-prop";
function MyInput({ value, defaultValue, onChange }) {
const [inputValue, setInputValue, isControlled] = useControlledProp(
value,
defaultValue,
"",
);
const handleChange = e => {
const newValue = e.target.value;
setInputValue(newValue); // Only works when uncontrolled
onChange?.(newValue);
};
return (
<div>
<input
value={inputValue}
onChange={handleChange}
/>
<p>Mode: {isControlled ? "Controlled" : "Uncontrolled"}</p>
</div>
);
}Controlled Usage
function App() {
const [value, setValue] = useState("controlled");
return (
<MyInput
value={value}
onChange={setValue}
/>
);
}Uncontrolled Usage
function App() {
return (
<MyInput
defaultValue="uncontrolled"
onChange={value => console.log("Changed:", value)}
/>
);
}With Custom Fallback
function MySelect({ value, defaultValue, options, onChange }) {
const [selectedValue, setSelectedValue] = useControlledProp(
value,
defaultValue,
options[0]?.value || "", // Fallback to first option
);
return (
<select
value={selectedValue}
onChange={e => {
setSelectedValue(e.target.value);
onChange?.(e.target.value);
}}
>
{options.map(option => (
<option
key={option.value}
value={option.value}
>
{option.label}
</option>
))}
</select>
);
}Boolean Props
function MyCheckbox({ checked, defaultChecked, onChange }) {
const [isChecked, setIsChecked, isControlled] = useControlledProp(
checked,
defaultChecked,
false,
);
return (
<input
type="checkbox"
checked={isChecked}
onChange={e => {
setIsChecked(e.target.checked);
onChange?.(e.target.checked);
}}
/>
);
}API
useControlledProp<T>(controlledValueProp, defaultValueProp, fallbackValue)
Parameters
controlledValueProp: T | undefined- The controlled value from props. When defined, the component operates in controlled modedefaultValueProp: T | undefined- The default value for uncontrolled modefallbackValue: T- The fallback value used when both controlled and default values are undefined
Returns
Returns a tuple [value, setUncontrolledValue, isControlled]:
value: T- The current value (controlled value or internal state)setUncontrolledValue: Dispatch<SetStateAction<T>>- Setter function that only works in uncontrolled modeisControlled: boolean- Whether the component is operating in controlled mode
Behavior
- Controlled Mode: When
controlledValuePropis notundefined, the hook returns the controlled value and the setter becomes a no-op - Uncontrolled Mode: When
controlledValuePropisundefined, the hook manages internal state usingdefaultValueProporfallbackValue - Mode Stability: The controlled/uncontrolled mode is determined on first render and cannot change during the component's lifetime
Development Warnings
The hook provides helpful warnings in development mode:
- Warning when switching between controlled and uncontrolled modes
- Warning when changing the default value of an uncontrolled component
- Warning when all values (controlled, default, and fallback) are undefined
Contributing
Read the contributing guide to learn about our development process, how to propose bug fixes and improvements, and how to build and test your changes.
License
This project is licensed under the terms of the MIT license.
