Lesson 19 / 25

Avoiding Re-renders and Heavy JS Work

Keep the JS thread responsive.

Why the JS thread matters

Your React code, state updates and most event handlers run on the JavaScript thread. If it is busy for longer than a frame (about 16 ms at 60 Hz), taps feel delayed and JS-driven animations stutter. Common causes are re-rendering large trees on every change, expensive calculations during render, and processing big JSON payloads. Remedies: keep state close to where it is used, split contexts, memoise expensive values with useMemo, keep callback identities stable with useCallback for memoised children, wrap list rows in React.memo, virtualise lists, paginate data and move heavy work to the server. The React Compiler can add memoisation automatically in supported setups; check the docs for your version. Always measure with the profiler before and after, and test performance in release builds, since development mode is much slower.

Smooth at 60 frames per second

Keep the JavaScript thread free, animate off it where possible, and ship lean assets on a fast engine.

Three ideas: rendering and JS work, animations, images, size and Hermes.
Figure 7.1 — Threads, animations and app size.

Memoising an expensive derived value

Compute once per input change, not on every render.

import { memo, useMemo, useState } from "react";
import { Text, TextInput, View } from "react-native";

type Tx = { id: string; amount: number; category: string };

const Summary = memo(function Summary({ total }: { total: number }) {
  return <Text>Total: {total.toFixed(2)}</Text>;
});

export function Spending({ transactions }: { transactions: Tx[] }) {
  const [note, setNote] = useState("");   // typing here should not recompute totals

  const total = useMemo(
    () => transactions.reduce((sum, t) => sum + t.amount, 0),
    [transactions],
  );

  return (
    <View>
      <Summary total={total} />
      <TextInput value={note} onChangeText={setNote} placeholder="Add a note" />
    </View>
  );
}

Remove console.log in production

Many log calls with large objects slow the JS thread. Strip or gate them in release builds, for example with a Babel plugin or a logger with levels.

Quick check: Why can a long computation in a press handler make an app feel frozen?

  • It forces the app to reinstall
  • It deletes the native views
  • It disables the GPU
  • It blocks the JavaScript thread, delaying updates and JS-driven animations
Answer

It blocks the JavaScript thread, delaying updates and JS-driven animations — One busy thread delays everything that depends on it.