Разработка · 7 мин чтения

Код-ревью React-проекта: что проверить перед приёмкой

Когда ко мне приходят с готовым React-проектом на приёмку - свой подрядчик закончил работу, фрилансер сдал MVP или агентство передаёт клиенту сайт - я прогоняю один и тот же маршрут проверки. Код-ревью react проекта занимает у меня от 3 до 8 часов в зависимости от размера кодовой базы, и почти всегда всплывает что-то, что не видно на глаз при беглом клике по интерфейсу: утечки памяти в useEffect, компоненты на 600 строк без разбивки, состояние, которое дублируется в трёх местах одновременно.

Структура проекта и организация компонентов

Первое, на что смотрю - это как разложены файлы. Если в src лежит один общий components/ на 80 файлов без группировки по фичам, это не катастрофа, но сигнал, что дальше поддержка будет стоить дороже с каждым спринтом. На проектах, где я принимал CRM-панели на React (у меня такие стоят от 100 000 ₽ под ключ), нормальной считаю структуру по доменам: orders/, clients/, auth/ - каждая папка со своими компонентами, хуками и типами внутри.

Смотрю отдельно на:

  • Глубину вложенности компонентов - если JSX уходит на 6-7 уровней в одном файле, значит никто не выносил суб-компоненты;
  • Именование - Button.jsx рядом с button-list.js в одной папке говорит об отсутствии договорённостей в команде;
  • Барабанные экспорты (index.ts с реэкспортом всего подряд) - удобно писать, неудобно потом трейсить, откуда взялся импорт при поиске бага.

Если структура хаотичная, я сразу закладываю в оценку доработки лишние 15-20% времени - переезд файлов по фичам почти никогда не делают попутно, откладывают до дедлайна и потом не делают вообще.

Хуки и логика компонентов - где прячутся самые частые баги

Props drilling через 4 уровня, useEffect без массива зависимостей или с зависимостями, которые меняются на каждый рендер (объекты и функции без useMemo/useCallback) - это то, что я нахожу почти в каждом втором ревью. Отдельно проверяю:

Кастомные хуки

Если в проекте есть useFetchOrders, useAuth, useDebounce - смотрю, не тянут ли они побочные эффекты друг из друга по цепочке, из-за которой один ре-рендер родителя триггерит три сетевых запроса подряд. На одном проекте с интеграцией эквайринга через Т‑Банк я нашёл именно так: хук статуса оплаты дергал API на каждый ре-рендер родительского компонента корзины, потому что зависимость useEffect была объект, а не строка с id заказа.

Условный рендеринг

Множественные тернарники внутри JSX (условие ? условие2 ? a : b : c) читать тяжело и ревьюеру, и через полгода автору. Прошу выносить в отдельные компоненты или ранний return.

// было
return (
  <div>
    {isLoading ? <Spinner /> : error ? <ErrorBox /> : data ? <List data={data} /> : null}
  </div>
);

// стало
if (isLoading) return <Spinner />;
if (error) return <ErrorBox />;
if (!data) return null;
return <List data={data} />;

Бесплатный материал

🎁 Полезный скрипт в подарок

Подпишитесь на Telegram - пришлю готовый скрипт по этой теме.

Без спама. Отписка в 1 клик.

Управление состоянием: где искать дорогие ошибки

Состояние - это то место, где 70% багов в продакшене рождаются на этапе разработки и всплывают только под нагрузкой реальных пользователей. Проверяю три вещи по порядку.

Первое - источник правды. Если данные заказа хранятся одновременно в Redux/Zustand-сторе, в локальном useState компонента и в React Query кэше, рано или поздно они разойдутся, и пользователь увидит устаревшую сумму в корзине. Второе - мутации state напрямую вместо иммутабельного обновления, особенно в связке с useState для массивов и объектов: push в массив вместо создания нового - классика, которая не ломает тесты, но ломает рендер в проде на React 18 из-за батчинга обновлений.

Третье - избыточный глобальный стор. Видел проекты, где в Redux тащат состояние открытого модального окна одной формы - это лишний boilerplate и лишний повод для ререндеров всего дерева подписчиков. Для локального UI-состояния хватает useState или useReducer внутри компонента.

Симптом Вероятная причина Как чиню
Данные «мигают» старыми значениями при навигации Дублирование state в сторе и локально Оставляю один источник, остальное - производные значения через селекторы
Список не обновляется после удаления элемента Мутация массива вместо filter/map Переписываю на иммутабельные операции
Модалка есть в Redux DevTools Глобальный стор для локального UI Переношу в useState компонента-владельца

Производительность и лишние ре-рендеры

Открываю React DevTools Profiler и прогоняю типовые сценарии - переключение вкладок, ввод текста в поиске, добавление товара в корзину. Если при вводе одного символа в поиск перерисовывается весь список из 200 карточек - ищу, где не расставлены React.memo, useMemo, useCallback, либо контекст (Context API) обновляется целиком вместо разделения на несколько мелких провайдеров.

Отдельно смотрю на списки без key или с key={index} - на статичных списках это терпимо, но на списках с сортировкой и удалением это гарантированный баг с перепутанными строками формы. И на бандл - если в проекте вебпак-конфиг тянет весь lodash или moment.js целиком ради одной функции форматирования даты, предлагаю заменить на date-fns с tree-shaking или на нативный Intl.DateTimeFormat.

При интеграции автоматизаций через n8n или ботов, которые дергают тот же бэкенд, что и React-фронт, дополнительно проверяю, не завязана ли пагинация фронта на полную выгрузку данных - на одном проекте с СДЭК-интеграцией фронт CRM грузил все заказы разом вместо серверной пагинации, и админка зависала на 3-4 секунды при каждом открытии раздела.

Типизация, тесты и обработка ошибок

Если проект на TypeScript, смотрю на количество any и @ts-ignore - больше 10-15 на средний проект уже повод переспросить, зачем вообще подключали типизацию. Проверяю, есть ли типы у пропсов компонентов или всё завязано на PropTypes образца 2018 года без строгой проверки.

По тестам не жду 100% покрытия - это нереалистично для большинства коммерческих проектов с бюджетом. Но критичные пути (оформление заказа, авторизация, оплата) должны быть закрыты хотя бы интеграционными тестами через React Testing Library. Смотрю, тестируют ли поведение пользователя (клик, ввод, ожидаемый результат) или реализацию (проверка внутреннего state компонента) - второе ломается при любом рефакторинге и не даёт реальной уверенности.

Обработка ошибок - Error Boundary на уровне разделов приложения (не одна на весь app), плюс проверка, что сетевые ошибки API показывают пользователю понятное сообщение, а не белый экран. На проектах с оплатой через эквайринг это критично: если запрос статуса платежа падает без обработки, пользователь не понимает, списались деньги или нет, и пишет в поддержку.

Безопасность на фронтенде React

DangerouslySetInnerHTML без санитайзера - первое, что ищу через grep по кодовой базе. Если контент с бэкенда или от пользователя (комментарии, описание товара из CMS) выводится напрямую в DOM, это открытая дверь для XSS. Проверяю, хранятся ли токены авторизации в localStorage (уязвимо к XSS) или в httpOnly cookie, и не светятся ли в бандле API-ключи третьих сторон - секреты для платёжных провайдеров или карт должны прокидываться через переменные окружения сборки, а не быть зашиты в коде компонента.

Если своей команде на разбор всего этого списка не хватает времени или опыта именно с React, такой аудит можно заказать отдельно - я делаю технический аудит и приёмку React-проекта перед тем, как клиент подпишет акт с подрядчиком.

Чек-лист код-ревью react проекта перед приёмкой

  • Структура файлов разложена по фичам, а не по типам файлов вперемешку
  • Хуки не тянут скрытые побочные эффекты друг из друга
  • Состояние хранится в одном месте, нет дублирования между стором и локальным state
  • Списки рендерятся с уникальным key, тяжёлые компоненты обёрнуты в memo
  • TypeScript используется по назначению, any не встречается пачками
  • Критичные сценарии (оплата, авторизация, оформление заказа) закрыты тестами
  • Есть Error Boundary и обработка сетевых ошибок с понятным UI
  • Нет XSS-дыр через dangerouslySetInnerHTML и токенов в localStorage без необходимости

Разобраться перед стартом

Консультация

от 3 000 ₽

Подробнее →

Частые вопросы

Сколько времени занимает код-ревью react проекта средней CRM-панели?

На проект на 40-60 компонентов у меня уходит 4-6 часов на полный проход по чек-листу с фиксацией замечаний в отчёте. Если проект больше 150 компонентов или есть сложная бизнес-логика (эквайринг, интеграции с СДЭК, склад), закладываю 8-10 часов.

Можно ли провести код-ревью без доступа к серверу и базе данных?

Да, для фронтенд-части это не требуется - достаточно доступа к репозиторию, package.json и, желательно, тестового стенда, чтобы прогнать сценарии через React DevTools Profiler. Доступ к бэкенду нужен только если проверяю сквозные сценарии вроде оплаты или интеграции с эквайрингом.

Что делать, если ревью выявило критичные проблемы, а проект уже нужно принимать по договору?

Фиксирую находки в отчёте с приоритетами (критично / желательно / на будущее) и передаю заказчику до подписания акта - это аргумент для переговоров по срокам доработки или удержанию части оплаты подрядчику до исправления критичных пунктов.

Нужен ли код-ревью, если проект писала одна и та же команда, которая будет его поддерживать дальше?

Всё равно полезно как независимый взгляд со стороны - своя команда часто не замечает накопленные компромиссы, принятые под дедлайн, потому что для них это привычный код. Внешний ревью находит то, что глаз замылил.

Есть задача?

Обсудим в мессенджере

Расскажите, что нужно сделать — отвечу в течение 4 часов в рабочее время. Первая консультация бесплатно.

Самозанятый Калинкин Н. А. · работаю с физлицами и юрлицами

Продолжая пользование настоящим сайтом Вы выражаете своё согласие на обработку Ваших персональных данных (файлов куки) с использованием Yandex.Metrika.
Понятно