From d92f4bb77fbbd75956837f57b33531dbc73542d6 Mon Sep 17 00:00:00 2001 From: doistbot Date: Tue, 24 Feb 2026 02:08:41 +0000 Subject: [PATCH] chore: Update React development guidelines This file is automatically synced from the `shared-configs` repository. Source: https://github.com/doist/shared-configs/blob/main/ --- docs/react-guidelines/async.md | 21 ++++++++------------- docs/react-guidelines/react-compiler.md | 4 +++- docs/react-guidelines/state.md | 5 +++-- docs/react-guidelines/testing.md | 2 +- 4 files changed, 15 insertions(+), 17 deletions(-) diff --git a/docs/react-guidelines/async.md b/docs/react-guidelines/async.md index b6e112e8..7f67f8fe 100644 --- a/docs/react-guidelines/async.md +++ b/docs/react-guidelines/async.md @@ -54,25 +54,20 @@ function ProjectPage({ projectId }: { projectId: string }) { ## Debouncing and Throttling -Debounce user input that triggers expensive operations. Keep references stable. +Debounce user input that triggers expensive operations. ```typescript function useSearchTasks() { + const dispatch = useAppDispatch() const [query, setQuery] = useState('') - const debouncedSearch = useMemo( - () => debounce((value: string) => dispatch(searchTasks(value)), 300), - [dispatch], - ) + const debouncedSearch = debounce((value: string) => dispatch(searchTasks(value)), 300) - const handleChange = useCallback( - (event: React.ChangeEvent) => { - const value = event.target.value - setQuery(value) - debouncedSearch(value) - }, - [debouncedSearch], - ) + function handleChange(event: React.ChangeEvent) { + const value = event.target.value + setQuery(value) + debouncedSearch(value) + } // Clean up on unmount useEffect( diff --git a/docs/react-guidelines/react-compiler.md b/docs/react-guidelines/react-compiler.md index 280c2d62..ba69122c 100644 --- a/docs/react-guidelines/react-compiler.md +++ b/docs/react-guidelines/react-compiler.md @@ -12,6 +12,8 @@ When React Compiler is enabled, **do not** use `useMemo`, `useCallback`, or `Rea If you find existing `useMemo` or `useCallback` calls in compiler-enabled code, they can be safely removed. See the [Mismatched useMemo dependencies](#mismatched-usememo-dependencies) fix pattern for guidance on handling existing manual memoization that the compiler flags. +> **Warning:** Do **not** remove manual memoization if the component still has compiler violations (check `.react-compiler.rec.json`) or if the compiler is not enabled in the project. In those cases the compiler is not optimizing the component, and removing `useMemo`/`useCallback` would lose memoization entirely. + ## Workflow: Identifying and fixing violations When working on React components or hooks in this codebase, follow this workflow: @@ -1107,7 +1109,7 @@ expect(renderCount).toBe(1) **After:** ```typescript -const onRender = jest.fn() +const onRender = jest.fn() // or vi.fn() in Vitest function TestComponent() { return
} diff --git a/docs/react-guidelines/state.md b/docs/react-guidelines/state.md index 755be221..65e2b302 100644 --- a/docs/react-guidelines/state.md +++ b/docs/react-guidelines/state.md @@ -111,7 +111,8 @@ Never export the store directly. Only export custom hooks. Consider using the `d ```typescript import { create } from 'zustand' -import { devtools, immer } from 'zustand/middleware' +import { devtools } from 'zustand/middleware' +import { immer } from 'zustand/middleware/immer' // Store is private const useFilterStore = create((set) => ({ @@ -173,7 +174,7 @@ actions: { export function useFilteredTasks() { const query = useFilterQuery() // Zustand const tasks = useAppSelector(selectAllTasks) // Redux - return useMemo(() => tasks.filter((t) => t.content.includes(query)), [tasks, query]) + return tasks.filter((t) => t.content.includes(query)) } // Zustand + Router diff --git a/docs/react-guidelines/testing.md b/docs/react-guidelines/testing.md index 4f0831ca..71dd71b2 100644 --- a/docs/react-guidelines/testing.md +++ b/docs/react-guidelines/testing.md @@ -5,7 +5,7 @@ Use React Testing Library with `userEvent` for interactions. Query by role, not test ID. ```typescript -import { screen } from '@testing-library/react' +import { render, screen } from '@testing-library/react' import userEvent from '@testing-library/user-event' test('completes a task when checkbox is clicked', async () => {