Skip to content

Instantly share code, notes, and snippets.

@devhammed
Last active February 3, 2026 11:53
Show Gist options
  • Select an option

  • Save devhammed/f0145624910849bd5722548ee9d4192f to your computer and use it in GitHub Desktop.

Select an option

Save devhammed/f0145624910849bd5722548ee9d4192f to your computer and use it in GitHub Desktop.
Use Controlled State hook manages a value that can be either controlled or uncontrolled. It returns the current state and a setter that updates internal state when uncontrolled and always calls an optional onChange callback.
import { useIsomorphicLayoutEffect } from '@/hooks/use-isomorphic-layout-effect';
import { useCallback, useRef, useState } from 'react';
import { flushSync } from 'react-dom';
export function useControlledState<T>(
initialValue: T,
controlledValue?: T,
onChange?: (value: T) => void,
): [T, (value: T | ((prev: T) => T)) => void] {
const [internalValue, setInternalValue] = useState(initialValue);
const isControlled = controlledValue !== undefined;
const stateRef = useRef<T>(initialValue);
const controlledRef = useRef<T | undefined>(controlledValue);
useIsomorphicLayoutEffect(() => {
stateRef.current = internalValue;
}, [internalValue]);
useIsomorphicLayoutEffect(() => {
controlledRef.current = controlledValue;
}, [controlledValue]);
return [
isControlled ? controlledValue : internalValue,
useCallback(
(next: T | ((prev: T) => T)) => {
const prev = isControlled ? controlledRef.current! : stateRef.current;
const resolved = typeof next === 'function' ? (next as (p: T) => T)(prev) : next;
queueMicrotask(() => {
if (!isControlled) {
// Ensure the internal state is up to date with the value before calling
// onChange. This allows you to submit forms as part of the `onChange`
// and gives enough time to update the form field value(s).
flushSync(() => setInternalValue(resolved));
}
onChange?.(resolved);
});
},
[isControlled, onChange, setInternalValue],
),
] as const;
}
import * as React from 'react';
const useIsomorphicLayoutEffect = typeof window !== 'undefined' ? React.useLayoutEffect : React.useEffect;
export { useIsomorphicLayoutEffect };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment