How to Avoid Expensive Calculations During React Rendering

Keep expensive work out of every render by moving it to module scope, deriving values instead of storing them, and memoizing only measured work.

6 min read

Expensive calculations in React run during every render, slowing the interface. React re-runs a component's body each time its state or props change, so avoiding that repeated work is the core of render performance.

The body runs even when the calculation's inputs are unchanged. The fix is to move the work out, or to skip it when its inputs are stable.

Measure before changing anything

Not every calculation is expensive. A filter over a dozen items costs nothing, while a loop over a hundred thousand rows can dominate a frame.

Wrap a suspect calculation in console.time and console.timeEnd, run the interaction, and check the logged time. Or record the interaction in the Profiler and look for the component with the largest render duration.

Measure in a production build on a device similar to your users, because development mode and Strict Mode inflate render times. A millisecond or more per update is a reasonable signal that the work is worth moving. A chart that recomputes a thousand data points on every keystroke is a candidate, while a label lookup is not.

Move static work out of the component

If a calculation does not depend on props or state, it does not belong inside the component. Move it to module scope so it runs once when the module loads.

App.jsxApp.jsx
const CATEGORY_LABELS = {
  tech: "Technology",
  design: "Design",
  business: "Business",
};
 
export default function ArticleCard({ category, title }) {
  return (
    <article>
      <span>{CATEGORY_LABELS[category]}</span>
      <h3>{title}</h3>
    </article>
  );
}

The lookup table is created once, outside the component. Every render of ArticleCard just reads a value from it instead of rebuilding the map.

The same rule applies to helpers that do not read props or state, such as formatting utilities or a static list of options. Hoisting them removes the cost entirely.

Compute derived values during render

When a value can be calculated from state or props, calculate it directly in the render body. Do not mirror it in state and keep it updated with an Effect.

App.jsxApp.jsx
import { useState } from "react";
 
export default function Cart() {
  const [items, setItems] = useState([]);
  const count = items.length;
 
  return <p>{count} items</p>;
}

The count is derived from items, so it is computed on every render for free. The alternative, storing count in state and updating it in an Effect, adds a second render and can drift out of sync.

A second render is exactly what you are trying to avoid. Deriving during render keeps one source of truth and removes the Effect entirely.

Memoize only measured work

When a calculation depends on props or state and is genuinely slow, cache it with useMemo so it only recomputes when its inputs change.

App.jsxApp.jsx
import { useMemo } from "react";
 
export default function Report({ transactions }) {
  const totals = useMemo(
    () => computeTotals(transactions),
    [transactions]
  );
 
  return <Summary totals={totals} />;
}

computeTotals loops over many transactions. With useMemo, the totals keep the same reference until transactions changes, so Summary can skip work too. The full difference between this and caching a function is covered in useMemo vs useCallback.

Only add this after measuring. useMemo has a small cost of its own, and it does not make the first render faster, only later updates.

Do work in event handlers when you can

A calculation that is triggered by a click does not need to run during render. Do it inside the handler, then store only the result in state. Rendering stays pure, and the work happens once per interaction instead of on every render.

Imagine a CSV export. Clicking Export builds the file in the handler and sets a ready state, so the expensive string generation never touches a render and the list stays responsive.

Let React Compiler do the work

React Compiler memoizes calculations inside components and hooks automatically at build time, so in many codebases you can skip the manual useMemo calls entirely. It is stable and optional, so you can adopt it incrementally.

When it is enabled, the compiler handles memoization for you. Measure either way, so you only change the part that is slow.

Rune AI

Rune AI

Key Insights

  • A component body runs on every render.
  • Move static work to module scope so it runs once.
  • Compute derived values during render instead of storing them.
  • Use useMemo only for measured, genuinely slow calculations.
  • React Compiler can memoize this work automatically.
RunePowered by Rune AI

Frequently Asked Questions

Should I wrap every calculation in useMemo?

No. useMemo helps only when a calculation is slow and its dependencies rarely change. Fast calculations do not need it, and the wrapper itself has a small cost.

Can I use an Effect to keep a derived value in state?

Avoid it. When a value can be computed from existing state or props during render, compute it directly. An Effect plus state adds a second render and can go stale.

Conclusion

Avoiding expensive render work starts with measuring, then moving work out of the component, deriving values during render, and memoizing only the calculations the Profiler proves are slow.