From a149e614a79a46b2c835917409fa246a4afd17fe Mon Sep 17 00:00:00 2001 From: Amruth Pillai Date: Sat, 4 Jul 2026 21:57:20 +0200 Subject: [PATCH] =?UTF-8?q?refactor(ui):=20finding=202=20=E2=80=94=20delet?= =?UTF-8?q?e=20packages/ui=20use-controlled-state=20(zero=20importers)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit apps/web has its own copy with 3 importers; the packages/ui copy had none. Deletes the hook and its 78-line test. Claude-Session: https://claude.ai/code/session_012Bnvt1MghwHj4qQRxuQUGa --- .../src/hooks/use-controlled-state.test.tsx | 78 ------------------- .../ui/src/hooks/use-controlled-state.tsx | 30 ------- 2 files changed, 108 deletions(-) delete mode 100644 packages/ui/src/hooks/use-controlled-state.test.tsx delete mode 100644 packages/ui/src/hooks/use-controlled-state.tsx diff --git a/packages/ui/src/hooks/use-controlled-state.test.tsx b/packages/ui/src/hooks/use-controlled-state.test.tsx deleted file mode 100644 index f5acded59..000000000 --- a/packages/ui/src/hooks/use-controlled-state.test.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import { act, renderHook } from "@testing-library/react"; -import { describe, expect, it, vi } from "vitest"; -import { useControlledState } from "./use-controlled-state"; - -describe("useControlledState", () => { - it("returns the defaultValue when uncontrolled", () => { - const { result } = renderHook(() => useControlledState({ defaultValue: 0 })); - expect(result.current[0]).toBe(0); - }); - - it("returns the value when controlled", () => { - const { result } = renderHook(() => useControlledState({ value: 42, defaultValue: 0 })); - expect(result.current[0]).toBe(42); - }); - - it("updates internal state when value prop changes (controlled)", () => { - const { result, rerender } = renderHook(({ value }: { value: number }) => useControlledState({ value }), { - initialProps: { value: 1 }, - }); - - expect(result.current[0]).toBe(1); - rerender({ value: 2 }); - expect(result.current[0]).toBe(2); - }); - - it("updates state via setter when uncontrolled", () => { - const { result } = renderHook(() => useControlledState({ defaultValue: 0 })); - act(() => { - result.current[1](10); - }); - expect(result.current[0]).toBe(10); - }); - - it("calls onChange when state is updated", () => { - const onChange = vi.fn(); - const { result } = renderHook(() => useControlledState({ defaultValue: "a", onChange })); - - act(() => { - result.current[1]("b"); - }); - - expect(onChange).toHaveBeenCalledWith("b"); - }); - - it("forwards extra args to onChange", () => { - const onChange = vi.fn(); - const { result } = renderHook(() => useControlledState({ defaultValue: "a", onChange })); - - act(() => { - result.current[1]("b", 42, true); - }); - - expect(onChange).toHaveBeenCalledWith("b", 42, true); - }); - - it("does not call onChange when value prop changes from outside", () => { - const onChange = vi.fn(); - const { rerender } = renderHook(({ value }: { value: number }) => useControlledState({ value, onChange }), { - initialProps: { value: 1 }, - }); - - rerender({ value: 2 }); - // onChange is only called via setState, not the controlled-value sync effect. - expect(onChange).not.toHaveBeenCalled(); - }); - - it("returns the same setter reference when onChange is stable", () => { - const onChange = vi.fn(); - const { result, rerender } = renderHook( - ({ value }: { value: number }) => useControlledState({ value, onChange }), - { initialProps: { value: 1 } }, - ); - - const initialSetter = result.current[1]; - rerender({ value: 2 }); - expect(result.current[1]).toBe(initialSetter); - }); -}); diff --git a/packages/ui/src/hooks/use-controlled-state.tsx b/packages/ui/src/hooks/use-controlled-state.tsx deleted file mode 100644 index a118aaef0..000000000 --- a/packages/ui/src/hooks/use-controlled-state.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { useCallback, useState } from "react"; - -interface CommonControlledStateProps { - value?: T; - defaultValue?: T; -} - -type UseControlledStateProps = CommonControlledStateProps & { - onChange?: (value: T, ...args: Rest) => void; -}; - -export function useControlledState( - props: UseControlledStateProps, -): readonly [T, (next: T, ...args: Rest) => void] { - const { value, defaultValue, onChange } = props; - const isControlled = value !== undefined; - - const [internalState, setInternalState] = useState(value !== undefined ? value : (defaultValue as T)); - const state = isControlled ? (value as T) : internalState; - - const setState = useCallback( - (next: T, ...args: Rest) => { - if (!isControlled) setInternalState(next); - onChange?.(next, ...args); - }, - [isControlled, onChange], - ); - - return [state, setState] as const; -}