Essential hooks for managing component state with stable references and optimized performance patterns.
Hook to store a boolean value and generate callbacks for setting the value to true or false. The identity of the callbacks will always stay the same, making it perfect for React.memo optimization.
/**
* Hook to store a value and generate callbacks for setting the value to true or false.
* The identity of the callbacks will always stay the same.
* @param initialState - Initial boolean value
* @returns Array with the current value and an object containing the updater callbacks
*/
function useBoolean(initialState: boolean): [boolean, IUseBooleanCallbacks];
interface IUseBooleanCallbacks {
/** Set the value to true. Always has the same identity. */
setTrue: () => void;
/** Set the value to false. Always has the same identity. */
setFalse: () => void;
/** Toggle the value. Always has the same identity. */
toggle: () => void;
}Usage Examples:
import { useBoolean } from "@fluentui/react-hooks";
function ToggleComponent() {
const [isVisible, { setTrue: show, setFalse: hide, toggle }] = useBoolean(false);
return (
<div>
<button onClick={show}>Show</button>
<button onClick={hide}>Hide</button>
<button onClick={toggle}>Toggle</button>
{isVisible && <div>This content is visible!</div>}
</div>
);
}
// Optimized with React.memo - callbacks won't cause re-renders
const OptimizedChild = React.memo(({ onToggle }: { onToggle: () => void }) => {
return <button onClick={onToggle}>Toggle from child</button>;
});
function ParentComponent() {
const [isActive, { toggle }] = useBoolean(false);
return (
<div>
<OptimizedChild onToggle={toggle} />
{isActive && <div>Active state</div>}
</div>
);
}Hook to initialize and return a constant value. Unlike React.useMemo, this is guaranteed to always return the same value and only call the initializer function once. Similar to setting a private member in a class constructor.
/**
* Hook to initialize and return a constant value. Unlike React.useMemo, this is guaranteed to
* always return the same value (and if the initializer is a function, only call it once).
* @param initialValue - Initial value, or function to get the initial value. Only the value/function
* passed in the first time this is called is respected.
* @returns The value. The identity of this value will always be the same.
*/
function useConst<T>(initialValue: T | (() => T)): T;Usage Examples:
import { useConst } from "@fluentui/react-hooks";
function ExpensiveComponent() {
// Expensive object creation - only runs once
const expensiveObject = useConst(() => ({
data: performExpensiveCalculation(),
cache: new Map(),
timestamp: Date.now()
}));
// Simple constant value
const componentId = useConst(() => `component-${Math.random()}`);
// Function constant
const stableCallback = useConst(() => () => {
console.log("This callback never changes");
});
return (
<div id={componentId}>
<div>Expensive data: {expensiveObject.data}</div>
<button onClick={stableCallback}>Stable callback</button>
</div>
);
}Hook to manage a value that could be either controlled or uncontrolled, such as a checked state or text box string. This pattern is essential for creating components that can work both in controlled and uncontrolled modes.
/**
* Hook to manage a value that could be either controlled or uncontrolled
* @param controlledValue - The controlled value passed in props. Always used if provided.
* @param defaultUncontrolledValue - Initial value for internal state in uncontrolled case.
* @returns Array of current value and updater callback with stable identity.
*/
function useControllableValue<TValue, TElement extends HTMLElement>(
controlledValue: TValue | undefined,
defaultUncontrolledValue: TValue | undefined
): Readonly<[TValue | undefined, (update: React.SetStateAction<TValue | undefined>) => void]>;
/**
* Overload with onChange callback for event handling
* @param controlledValue - The controlled value passed in props
* @param defaultUncontrolledValue - Initial value for internal state
* @param onChange - Callback fired when value changes
* @returns Array of current value and updater callback that calls onChange
*/
function useControllableValue<
TValue,
TElement extends HTMLElement,
TEvent extends React.SyntheticEvent<TElement> | undefined
>(
controlledValue: TValue | undefined,
defaultUncontrolledValue: TValue | undefined,
onChange: ChangeCallback<TElement, TValue, TEvent> | undefined
): Readonly<[
TValue | undefined,
(update: React.SetStateAction<TValue | undefined>, ev?: React.FormEvent<TElement>) => void
]>;
type ChangeCallback<
TElement extends HTMLElement,
TValue,
TEvent extends React.SyntheticEvent<TElement> | undefined
> = (ev: TEvent, newValue: TValue | undefined) => void;Usage Examples:
import { useControllableValue } from "@fluentui/react-hooks";
// Basic controllable input component
interface TextInputProps {
value?: string;
defaultValue?: string;
onChange?: (event: React.ChangeEvent<HTMLInputElement>, newValue: string | undefined) => void;
}
function TextInput({ value, defaultValue, onChange }: TextInputProps) {
const [currentValue, setValue] = useControllableValue(value, defaultValue, onChange);
const handleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
setValue(event.target.value, event);
};
return (
<input
type="text"
value={currentValue || ""}
onChange={handleChange}
/>
);
}
// Usage as controlled component
function ControlledExample() {
const [text, setText] = useState("controlled");
return (
<TextInput
value={text}
onChange={(ev, newValue) => setText(newValue || "")}
/>
);
}
// Usage as uncontrolled component
function UncontrolledExample() {
return (
<TextInput
defaultValue="uncontrolled"
onChange={(ev, newValue) => console.log("Changed to:", newValue)}
/>
);
}/**
* @deprecated Deprecated due to potential for misuse. Generally, use React.useCallback instead.
* If you need a callback reference that never changes, consider useEventCallback.
*
* This hook was intended for creating callbacks which have no dependencies, and therefore never
* need to change. Usage tends to result in bugs like unintentionally capturing the first value
* of a prop and not respecting updates.
*/
function useConstCallback<T extends (...args: any[]) => any>(callback: T): T;Migration Guide:
// ❌ Deprecated useConstCallback
const callback = useConstCallback(() => {
// This captures the initial value and never updates
console.log(someProp);
});
// ✅ Use useEventCallback instead
const callback = useEventCallback(() => {
// This always uses the latest value
console.log(someProp);
});
// ✅ Or use React.useCallback with proper dependencies
const callback = useCallback(() => {
console.log(someProp);
}, [someProp]);