Thursday, June 26, 2025

React: Memoized unchanged properties

This demonstrates that it is expensive to re-render/re-process a variable when its value is not changing
import { memo, useMemo, useState } from 'react';
import './App.css';

type Person = {
  firstName: string;
  lastName: string;
};

type WithInitial = Person & {
  initials: string;
};

const initialPeople: Person[] = [
  { firstName: 'John', lastName: 'Lennon' },
  { firstName: 'Paul', lastName: 'McCartney' },
  { firstName: 'George', lastName: 'Harrison' },
  { firstName: 'Ringo', lastName: 'Starr' },
];

function App() {
  const [theme, setTheme] = useState('light');
  const [people, setPeople] = useState(initialPeople);

  const [counter, setCounter] = useState(0);

  return (
    <div>
      <button onClick={() => setCounter((prev) => prev + 1)}>
        Increase {counter}
      </button>
      <button onClick={toggleTheme}>Toggle {theme}</button>
      <button onClick={addRandomPerson}>Add Random Person</button>
      <ListComponentNoMemo {...{ theme, people }} />
      <ListComponentWithMemo {...{ theme, people }} />
      <MemoListComponentWithMemo {...{ theme, people }} />
    </div>
  );

  function toggleTheme() {
    setTheme((prev) => (prev === 'light' ? 'dark' : 'light'));
  }

  function addRandomPerson() {
    setPeople((prev) => [
      ...prev,
      {
        firstName: Math.ceil(Math.random() * 100).toString(),
        lastName: Math.ceil(Math.random() * 100).toString(),
      },
    ]);
  }
}

function ListComponentNoMemo({
  theme,
  people,
}: {
  theme: string;
  people: Person[];
}) {
  console.log('ListComponentNoMemo');
  const withInitials = processInitials('noMemo', people);

  return (
    <>
      <p>Theme is {theme}</p>
      <ul>
        {withInitials.map((e, index) => (
          <li key={index}>
            {e.firstName} {e.lastName} {e.initials}
          </li>
        ))}
      </ul>
    </>
  );
}

function ListComponentWithMemo({
  theme,
  people,
}: {
  theme: string;
  people: Person[];
}) {
  console.log('ListComponenWithMemo');
  const withInitials = useMemo(
    () => processInitials('withMemo', people),
    [people]
  );

  return (
    <>
      <p>Theme is {theme}</p>
      <ul>
        {withInitials.map((e, index) => (
          <li key={index}>
            {e.firstName} {e.lastName} {e.initials}
          </li>
        ))}
      </ul>
    </>
  );
}

const MemoListComponentWithMemo = memo(function ({
  theme,
  people,
}: {
  theme: string;
  people: Person[];
}) {
  console.log('MemoListComponenWithMemo');
  const withInitials = useMemo(
    () => processInitials('memoWithMemo', people),
    [people]
  );

  return (
    <>
      <p>Theme is {theme}</p>
      <ul>
        {withInitials.map((e, index) => (
          <li key={index}>
            {e.firstName} {e.lastName} {e.initials}
          </li>
        ))}
      </ul>
    </>
  );
});

function processInitials(calledFrom: string, people: Person[]): WithInitial[] {
  console.log({ calledFrom });
  return people.map((e) => ({
    firstName: e.firstName,
    lastName: e.lastName,
    initials: toInitials(e),
  }));

  function toInitials(p: Person) {
    console.log('emphasized expensiveness');
    return p.firstName[0] + p.lastName[0];
  }
}

export default App;
Live: https://stackblitz.com/edit/vitejs-vite-drznsqwc?file=src%2FApp.tsx

No comments:

Post a Comment