feat(web): useSearch infinite query + useDebouncedValue + MSW search handler
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,24 @@
|
||||
import { useState } from "react";
|
||||
import { expect, test } from "vitest";
|
||||
import { screen } from "@testing-library/react";
|
||||
import userEvent from "@testing-library/user-event";
|
||||
import { renderApp } from "../test/render";
|
||||
import { useDebouncedValue } from "./use-debounced-value";
|
||||
|
||||
function Harness() {
|
||||
const [text, setText] = useState("");
|
||||
const debounced = useDebouncedValue(text, 150);
|
||||
return (
|
||||
<div>
|
||||
<input aria-label="in" value={text} onChange={(e) => setText(e.target.value)} />
|
||||
<span data-testid="out">{debounced}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
test("reflects the value after the delay", async () => {
|
||||
renderApp(<Harness />);
|
||||
await userEvent.type(screen.getByLabelText("in"), "bronze");
|
||||
await screen.findByText("bronze");
|
||||
expect(screen.getByTestId("out")).toHaveTextContent("bronze");
|
||||
});
|
||||
@@ -0,0 +1,14 @@
|
||||
import { useEffect, useState } from "react";
|
||||
|
||||
/** Returns `value` delayed by `delayMs`; resets the timer on each change. */
|
||||
export function useDebouncedValue<T>(value: T, delayMs: number): T {
|
||||
const [debounced, setDebounced] = useState(value);
|
||||
|
||||
useEffect(() => {
|
||||
const id = setTimeout(() => setDebounced(value), delayMs);
|
||||
|
||||
return () => clearTimeout(id);
|
||||
}, [value, delayMs]);
|
||||
|
||||
return debounced;
|
||||
}
|
||||
Reference in New Issue
Block a user