{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lib-use-controllable-state",
  "type": "registry:lib",
  "title": "lib/use-controllable-state",
  "description": "controlled(value) / uncontrolled(defaultValue) 를 모두 지원하는 상태 훅",
  "files": [
    {
      "path": "poc/design-system/implement/src/lib/use-controllable-state.ts",
      "content": "import { useCallback, useLayoutEffect, useRef, useState } from \"react\";\n\ntype Options<T> = {\n  value?: T;\n  defaultValue: T;\n  onChange?: (value: T) => void;\n};\n\n/** controlled(value) / uncontrolled(defaultValue) 를 모두 지원하는 상태 훅 */\nexport function useControllableState<T>({ value, defaultValue, onChange }: Options<T>) {\n  const [inner, setInner] = useState<T>(defaultValue);\n  const isControlled = value !== undefined;\n  const current = isControlled ? value : inner;\n\n  // 최신 onChange 를 렌더가 끝난 뒤 ref 에 반영 (렌더 중 ref 쓰기 금지)\n  const onChangeRef = useRef(onChange);\n  useLayoutEffect(() => {\n    onChangeRef.current = onChange;\n  });\n\n  const setValue = useCallback(\n    (next: T) => {\n      if (!isControlled) setInner(next);\n      onChangeRef.current?.(next);\n    },\n    [isControlled],\n  );\n\n  return [current, setValue] as const;\n}\n",
      "type": "registry:lib",
      "target": "components/lib/use-controllable-state.ts"
    }
  ],
  "categories": [
    "internal"
  ],
  "meta": {
    "version": "0.1.0"
  }
}
