public-rules › React techs/react

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) or useState(props.value), does not need it.
  • For useReducer, pass an init function 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.