React Memo & Code Splitting

Maximize rendering performance using React.memo, custom comparison heuristics, code splitting, and React Compiler optimizations.

TL;DR

  1. Skip redundant component re-renders using the React.memo wrapper.
  2. Customize prop equality comparisons with custom arePropsEqual functions.
  3. Split application bundles on demand with dynamic lazy imports.

Component Memoization with memo

    Basic memo Wrapper

    Prevent re-renders when scalar props remain identical.

    export const MemoRow = memo(function Row({
      name,
      value,
    }: RowProps) {
      return <Text>{name}: {value}</Text>;
    });
    Custom Comparison Function

    Supply custom comparator evaluating only specific keys.

    function arePropsEqual(prev: Props, next: Props) {
      return prev.user.id === next.user.id;
    }
    export const UserCard = memo(CardView, arePropsEqual);
    Display Name for DevTools

    Assign displayName to retain debugging visibility.

    MemoRow.displayName = 'MemoRow';

Code Splitting with React.lazy

    Dynamic Route Splitting

    Split application bundle at route level.

    const Settings = lazy(() => import('./Settings'));
    return (
      <Suspense fallback={<PageSpinner />}>
        <Settings />
      </Suspense>
    );
    Modal on Demand

    Load dialog chunk only when user clicks open.

    const HeavyModal = lazy(() => import('./Modal'));
    return (
      <Suspense fallback={null}>
        {isOpen && <HeavyModal onClose={close} />}
      </Suspense>
    );
    Named Export Import Adapter

    Import named export with intermediate resolution.

    const Chart = lazy(() =>
      import('./Charts')
        .then((m) => ({ default: m.Chart }))
    );

React Compiler Architecture

    Automatic Memoization

    Compiler infers dependencies and memoizes build.

    // No manual useMemo/useCallback required
    function ProductList({ items }) {
      const list = items.filter(isValid);
      return <ListView items={list} />;
    }
    Rules of React Enforcement

    Compiler requires strict adherence to hook rules.

    // Must keep render functions completely pure
    // Never mutate existing variables in render
    Gradual Adoption

    Adopt compiler incrementally across specific paths.

    // babel.config.js
    plugins: [['babel-plugin-react-compiler', options]];

Memoization Pitfalls

    Inline Object Prop Bust

    Inline object creation destroys React.memo benefits.

    // BAD: new style object created every render
    <MemoCard style={{ color: 'red' }} />
    Inline Callback Prop Bust

    Arrow function prop defeats equality comparison.

    // BAD: new function reference created
    <MemoButton onClick={() => doAction()} />
    Children Prop Invalidation

    Passing JSX children breaks shallow prop equality.

    // BAD: JSX children always creates new object
    <MemoCard><Text>Content</Text></MemoCard>

Tips

  1. Always profile before wrapping components in React.memo; comparing props incurs small overhead that can exceed cheap render costs.
  2. Ensure props passed to React.memo components maintain stable referential identity across renders using useMemo and useCallback.

Warnings

  1. Passing inline functions or object literals into React.memo components breaks memoization because new references are generated on every render.
  2. Do not rely solely on manual memoization in new projects; prepare architecture for automated compilation via the React Compiler.

In Practice

FAQ