@@ -143,31 +143,79 @@ const [personalDetails] = useOnyx(ONYXKEYS.PERSONAL_DETAILS_LIST);
143143
144144---
145145
146- ### [ PERF-4] Memoize objects and functions passed as props
146+ ### [ PERF-4] Memoize objects (including arrays) and functions passed as props
147147
148- - ** Search patterns** : ` useMemo ` , ` useCallback ` , and prop passing patterns
148+ - ** Search patterns** : ` prop={{ ` , ` prop={[ ` , ` ={() => ` , ` prop={variable} ` (where variable is non-memoized object/function)
149149
150- - ** Condition** : Objects and functions passed as props should be properly memoized or simplified to primitive values to prevent unnecessary re-renders.
151- - ** Reasoning** : React uses referential equality to determine if props changed. New object/function instances on every render trigger unnecessary re-renders of child components, even when the actual data hasn't changed. Memoization preserves referential stability.
150+ - ** Applies ONLY to** : Objects (including arrays)/functions passed directly as JSX props. Does NOT apply to:
151+ - Code inside callbacks (` .then() ` , event handlers)
152+ - Code inside ` useEffect ` /` useMemo ` /` useCallback ` bodies
153+ - Primitives (strings, numbers, booleans)
154+ - Already memoized values (` useMemo ` /` useCallback ` )
152155
153- Good:
156+ - ** Reasoning ** : New object/function references break memoization of child components. Only matters when child IS memoized AND parent is NOT optimized by React Compiler.
154157
155- ``` tsx
156- const reportData = useMemo (() => ({
157- reportID: report .reportID ,
158- type: report .type ,
159- isPinned: report .isPinned ,
160- }), [report .reportID , report .type , report .isPinned ]);
158+ #### Before flagging: Run optimization check
159+
160+ ** YOU MUST call ` checkReactCompilerOptimization.ts ` (available in PATH from ` .claude/scripts/ ` ) on EVERY .tsx file from the diff.**
161161
162- return <ReportActionItem report = { reportData } />
162+ ** Call the script ONCE per file, separately. DO NOT use loops or batch processing.**
163+
164+ Example usage:
165+ ``` bash
166+ checkReactCompilerOptimization.ts src/components/File1.tsx
167+ checkReactCompilerOptimization.ts src/components/File2.tsx
163168```
164169
165- Bad:
170+ ** NEVER use absolute or relative paths for this script. Call it by name only:**
171+ - ✅ ` checkReactCompilerOptimization.ts src/components/Example.tsx `
172+ - ❌ ` /home/runner/work/App/App/.claude/scripts/checkReactCompilerOptimization.ts ... `
173+ - ❌ ` ./.claude/scripts/checkReactCompilerOptimization.ts ... `
174+
175+ ** "File not found"** → Assume parent is optimized and skip PERF-4.
176+
177+ #### Decision flow
178+
179+ 1 . ** Parent in ` parentOptimized ` ?** → YES = ** Skip** (compiler auto-memoizes)
180+
181+ 2 . ** Child has custom memo comparator that PREVENTS re-render for this prop?**
182+ → Use ` sourcePath ` from script output to read child's source file
183+ → Grep for ` React.memo ` or ` memo( `
184+ → If custom comparator prevents re-render despite new reference for this prop → ** Skip**
166185
186+ 3 . ** Child is memoized?** (` optimized: true ` OR ` React.memo ` )
187+ - NO → ** Skip** (child re-renders anyway)
188+ - YES → ** Flag PERF-4**
189+
190+ #### Examples
191+
192+ ** Flag** (parent NOT optimized, child IS memoized, no custom comparator):
193+ ``` tsx
194+ // Script output: parentOptimized: [], child MemoizedList optimized: true
195+ // No custom comparator found
196+ return <MemoizedList options = { { showHeader: true }} />;
197+ ```
198+
199+ ** Skip - custom comparator** (comparator prevents re-render for this prop):
167200``` tsx
168- const [report] = useOnyx (` ONYXKEYS.COLLECTION.REPORT${iouReport .id } ` );
201+ // Script output: sourcePath: "src/components/PopoverMenu.tsx"
202+ // PopoverMenu.tsx has custom memo comparator that handles anchorPosition
203+ return <PopoverMenu anchorPosition = { {x: 0 , y: 0 }} />;
204+ ```
169205
170- return <ReportActionItem report = { report } />
206+ ** Skip - parent optimized** :
207+ ``` tsx
208+ // Script output: parentOptimized: ["MyComponent"]
209+ // React Compiler auto-memoizes - no manual memoization needed
210+ return <MemoizedList options = { { showHeader: true }} />;
211+ ```
212+
213+ ** Skip - spread props with stable inner values** :
214+ ``` tsx
215+ // Spread is OK when inner values come from memoized sources
216+ // illustration from useMemoizedLazyIllustrations, illustrationStyle from useThemeStyles
217+ const illustration = useAboutSectionIllustration ();
218+ return <Section { ... illustration } />;
171219```
172220
173221---
0 commit comments