Initialize expensive state lazily
LOW-MEDIUM1.0.0
When to apply Before writing, changing, or reviewing a React useState or useReducer call whose initial value is computed, such as parsing stored data or building an index.
When the initial state comes from a computation, pass a function to useState, such as useState(() => buildIndex(items)), instead of the computed value.
Implementation
- Use the function form for parsing, building data structures, and reading from storage or the DOM.
- A literal or an already available value, such as
useState(0)oruseState(props.value), does not need it. - For
useReducer, pass aninitfunction as the third argument. - Reading browser storage in an initializer still runs during server rendering; guard it or read it on the client.
Rationale
JavaScript evaluates a function call's arguments before the call, so useState(buildIndex(items)) runs buildIndex on every render.
React uses the argument only on the first render, so every later computation is wasted.
An initializer function runs only on the first render.
Examples
Incorrect (counterexample):
const [index, setIndex] = useState(buildSearchIndex(items));
Correct:
const [index, setIndex] = useState(() => buildSearchIndex(items));
Validation
Check useState and useReducer calls whose initial value is a function call or other non-trivial expression.
A cheap literal or variable passed directly is not a violation.