Dev Highlights

Доки по разработке

This project is maintained by teniryte

Производительность и инструменты

Этот файл описывает основные приёмы диагностики и оптимизации производительности React‑приложений, а также полезные инструменты для отладки.

Трассировка ререндеров

Первый шаг к оптимизации — понять, что именно ререндерится и когда. Простой приём — логировать рендеры компонентов.

function Message({ message }: { message: string }) {
  console.log('Render Message');
  return <div>{message}</div>;
}

export function PerfPage() {
  const [count, setCount] = useState(0);

  console.log('Render PerfPage');

  return (
    <>
      <h1>Performance</h1>
      <button onClick={() => setCount(c => c + 1)}>
        COUNT: {count}
      </button>

      <Message message="Это сообщение" />
    </>
  );
}

В таком виде при каждом клике будут ререндериться и PerfPage, и Message. Это не всегда плохо, но если компонент тяжёлый, стоит подумать об оптимизации.

React.memo для мемоизации компонентов

React.memo предотвращает ререндер дочернего компонента, если его пропсы поверхностно не изменились.

const MessageMemo = React.memo(function MessageMemo({
  message,
}: {
  message: string;
}) {
  console.log('Render MessageMemo');
  return <div>{message}</div>;
});

export function PerfPageOptimized() {
  const [count, setCount] = useState(0);

  return (
    <>
      <button onClick={() => setCount(c => c + 1)}>
        COUNT: {count}
      </button>

      {/* Этот компонент не будет ререндериться при изменении count */}
      <MessageMemo message="Статическое сообщение" />
    </>
  );
}

Важно: если вы передаёте функции или объекты, стоит мемоизировать и их (useCallback, useMemo), иначе React.memo не поможет.

Разделение состояния

Частая проблема — когда один useState/useReducer отвечает сразу за всё, и изменение одной части данных вызывает ререндер всего дерева.

Рекомендации:

export function SplitStateExample() {
  const [counter, setCounter] = useState(0);
  const [filter, setFilter] = useState('');

  return (
    <div>
      <button onClick={() => setCounter(c => c + 1)}>
        Counter: {counter}
      </button>

      <input
        value={filter}
        onChange={e => setFilter(e.target.value)}
        placeholder="Фильтр"
      />

      {/* Тяжёлый список можно мемоизировать и передавать только нужные данные */}
      <HeavyList filter={filter} />
    </div>
  );
}

Гидратация и работа с DOM

При серверном рендеринге React сначала создаёт HTML на сервере, а затем “гидрирует” его на клиенте — привязывает обработчики событий и превращает статический HTML в “живое” приложение.

Пример ручной гидратации:

import { renderToString } from 'react-dom/server';
import { hydrateRoot } from 'react-dom/client';

function App() {
  return <button onClick={() => alert('Click')}>Click me</button>;
}

// На сервере:
const html = renderToString(<App />);
// → html отправляется клиенту как строка

// На клиенте:
const container = document.getElementById('root')!;
container.innerHTML = html;

hydrateRoot(container, <App />);

Советы:

Инструменты отладки

React DevTools

React DevTools (расширение для браузера) позволяет:

Подход к профилированию:

  1. Включите Profiler и запишите взаимодействие (клики, ввод).
  2. Посмотрите, какие компоненты ререндерятся подозрительно часто.
  3. Решите, нужно ли применять оптимизации (React.memo, вынос состояния, useMemo, useCallback).

Консоль в браузере и на мобильных устройствах

Стандартная console.log остаётся простейшим способом понять, что происходит. На мобильных устройствах её сложнее смотреть, поэтому можно:

Пример крайне простой “консольки” внутри приложения:

function useLog() {
  const [logs, setLogs] = useState<string[]>([]);

  function log(message: string) {
    setLogs(prev => [...prev, message]);
  }

  return { logs, log };
}

export function PageWithLogs() {
  const { logs, log } = useLog();

  return (
    <div>
      <button onClick={() => log('Clicked!')}>Click</button>
      <pre style={{ maxHeight: 200, overflow: 'auto' }}>
        {logs.join('\n')}
      </pre>
    </div>
  );
}

Общие рекомендации по производительности