mirror of
https://github.com/AmruthPillai/Reactive-Resume.git
synced 2026-07-27 02:14:50 +10:00
refactor(ui): finding 2 — delete packages/ui use-controlled-state (zero importers)
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
This commit is contained in:
@@ -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<number>({ 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<string>({ 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<string, [number, boolean]>({ 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<number>({ 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<number>({ value, onChange }),
|
|
||||||
{ initialProps: { value: 1 } },
|
|
||||||
);
|
|
||||||
|
|
||||||
const initialSetter = result.current[1];
|
|
||||||
rerender({ value: 2 });
|
|
||||||
expect(result.current[1]).toBe(initialSetter);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
import { useCallback, useState } from "react";
|
|
||||||
|
|
||||||
interface CommonControlledStateProps<T> {
|
|
||||||
value?: T;
|
|
||||||
defaultValue?: T;
|
|
||||||
}
|
|
||||||
|
|
||||||
type UseControlledStateProps<T, Rest extends unknown[] = []> = CommonControlledStateProps<T> & {
|
|
||||||
onChange?: (value: T, ...args: Rest) => void;
|
|
||||||
};
|
|
||||||
|
|
||||||
export function useControlledState<T, Rest extends unknown[] = []>(
|
|
||||||
props: UseControlledStateProps<T, Rest>,
|
|
||||||
): readonly [T, (next: T, ...args: Rest) => void] {
|
|
||||||
const { value, defaultValue, onChange } = props;
|
|
||||||
const isControlled = value !== undefined;
|
|
||||||
|
|
||||||
const [internalState, setInternalState] = useState<T>(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;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user