react-doctor/rerender-memo-with-default-value
Move to module scope: `const EMPTY_ITEMS: Item[] = []` then use as the default value
- Category: Performance
- Severity: warn
- Source:
oxlint-plugin-react-doctor - Framework: global
- Enabled when: always
Validation prompt
Use this to decide whether a fired diagnostic is real or a false positive.
Confirm a PascalCase component destructures props with an inline default of an empty object literal ({ options = {} }) or empty array literal ({ items = [] }). Each render allocates a fresh reference, breaking downstream memo / useMemo / useEffect dependency checks that rely on referential equality.
Fix prompt
Use this once validation confirms the diagnostic is real.
Hoist the default to module scope: const EMPTY_ITEMS: readonly Item[] = []; then destructure as ({ items = EMPTY_ITEMS }). The same reference is reused across renders, restoring referential equality for memoized children and dependency arrays. See https://react.dev/reference/react/memo#updating-a-memoized-component-using-state
Related rules
More Performance rules from the rules reference:
react-doctor/rerender-state-only-in-handlers: Replace useState with useRef when the value is only mutated and never read in render — `ref.current = ...` updates without re-rendering the componentreact-doctor/rerender-transitions-scroll: Wrap the setState in startTransition (mark as non-urgent), use useDeferredValue, or stash in a ref + rAF throttle so scroll/pointer events don't trigger a re-render per firereact-doctor/tanstack-start-loader-parallel-fetch: Use `const [a, b] = await Promise.all([fetchA(), fetchB()])` to avoid request waterfalls in route loadersreact-doctor/advanced-event-handler-refs: Store the handler in a ref and have the listener read `handlerRef.current()` — the subscription stays put while the latest handler is always calledreact-doctor/async-await-in-loop: Collect the items and use `await Promise.all(items.map(...))` to run independent operations concurrently