Skills included: - pr-reviewer: Adapted for Gitea/GitHub via platform-aware scripts (dropped fetch_pr_data.py and add_inline_comment.py, kept generate_review_files.py) - code-review-excellence: Methodology and checklists (React, TS, Python, etc.) - vercel-react-best-practices: 57 rules for React/Next.js performance - tailwind-design-system: Tailwind CSS v4 patterns, CVA, design tokens New shell scripts added to ~/.claude/scripts/git/: - pr-diff.sh: Get PR diff (GitHub gh / Gitea API) - pr-metadata.sh: Get PR metadata as normalized JSON Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
59 lines
2.0 KiB
Markdown
59 lines
2.0 KiB
Markdown
---
|
|
title: Use Lazy State Initialization
|
|
impact: MEDIUM
|
|
impactDescription: wasted computation on every render
|
|
tags: react, hooks, useState, performance, initialization
|
|
---
|
|
|
|
## Use Lazy State Initialization
|
|
|
|
Pass a function to `useState` for expensive initial values. Without the function form, the initializer runs on every render even though the value is only used once.
|
|
|
|
**Incorrect (runs on every render):**
|
|
|
|
```tsx
|
|
function FilteredList({ items }: { items: Item[] }) {
|
|
// buildSearchIndex() runs on EVERY render, even after initialization
|
|
const [searchIndex, setSearchIndex] = useState(buildSearchIndex(items))
|
|
const [query, setQuery] = useState('')
|
|
|
|
// When query changes, buildSearchIndex runs again unnecessarily
|
|
return <SearchResults index={searchIndex} query={query} />
|
|
}
|
|
|
|
function UserProfile() {
|
|
// JSON.parse runs on every render
|
|
const [settings, setSettings] = useState(
|
|
JSON.parse(localStorage.getItem('settings') || '{}')
|
|
)
|
|
|
|
return <SettingsForm settings={settings} onChange={setSettings} />
|
|
}
|
|
```
|
|
|
|
**Correct (runs only once):**
|
|
|
|
```tsx
|
|
function FilteredList({ items }: { items: Item[] }) {
|
|
// buildSearchIndex() runs ONLY on initial render
|
|
const [searchIndex, setSearchIndex] = useState(() => buildSearchIndex(items))
|
|
const [query, setQuery] = useState('')
|
|
|
|
return <SearchResults index={searchIndex} query={query} />
|
|
}
|
|
|
|
function UserProfile() {
|
|
// JSON.parse runs only on initial render
|
|
const [settings, setSettings] = useState(() => {
|
|
const stored = localStorage.getItem('settings')
|
|
return stored ? JSON.parse(stored) : {}
|
|
})
|
|
|
|
return <SettingsForm settings={settings} onChange={setSettings} />
|
|
}
|
|
```
|
|
|
|
Use lazy initialization when computing initial values from localStorage/sessionStorage, building data structures (indexes, maps), reading from the DOM, or performing heavy transformations.
|
|
|
|
For simple primitives (`useState(0)`), direct references (`useState(props.value)`), or cheap literals (`useState({})`), the function form is unnecessary.
|