Главная / Статьи

Десять скрытых проблем компонентов React, замедляющих работу приложений в эпоху React 19

Десять распространённых ошибок компонентов React: от отсутствия семантического HTML до пропуска процедуры мемоизации; почему React 19 и React Compiler не скрывают их, а также способы их исправления.

Издатель: REACTAPP.TOP16 мин чтения3642 слов

Источники: 10 React Component Mistakes That Are Secretly Breaking Your App in 2026

Компоненты React кажутся простыми, пока небольшие ошибки не приводят к проблемам с доступностью, замедлению отрисовки и сложнообнаруживаемым багам. React 19, React Compiler и Server Components изменили способ создания приложений, из-за чего такие ошибки становятся ещё более дорогостоящими, а не менее. Ниже приведены десять ошибок на уровне компонентов, которые тихо вредят приложению, вместе с способами их устранения.

1. Использование <div> вместо семантического HTML

Ошибка: использование универсального элемента <div> для каждого элемента интерфейса. Навигация превращается в <div>, кнопка — в <div onClick={...}>, статья — в <div>; даже метки формы обрабатываются таким же образом.

// ❌ Bad: Div soup
function Navbar() {
  return (
    <div className="navbar">
      <div className="logo" onClick={() => router.push('/')}>Logo</div>
      <div className="nav-links">
        <div className="nav-item" onClick={() => setPage('home')}>Home</div>
        <div className="nav-item" onClick={() => setPage('about')}>About</div>
      </div>
    </div>
  );
}

Почему это портит ваше приложение:

  • Проблемы с доступностью: технологии для людей с ограниченными возможностями не могут распознавать ориентиры навигации, интерактивные элементы управления или заголовки. Человек, использующий считыватель экрана, слышит что-то вроде «кликабельный элемент, текст: Главная», вместо «элемент навигации, ссылка, Главная».
  • Нарушена работа с клавиатурой: теги <div> по умолчанию не являются фокусируемыми, поэтому пользователи, навигирующие с помощью клавиатуры, не могут переходить к меню навигации с помощью клавиши Tab.
  • Ухудшенный SEO: поисковые системы опираются на семантическую разметку, такую как <nav>, <main> и <article>, для анализа структуры страницы. Страница, полностью состоящая из тегов <div>, воспринимается поисковыми роботами как контент низкого качества.
  • Компоненты сервера в React 19: поскольку технологии SSR и стриминг означают, что первоначальный HTML-контент виден как пользователями, так и ботами до начала процесса гидратации, семантическая структура становится ещё более важной.

Решение:

// ✅ Good: Semantic HTML
function Navbar() {
  return (
    <nav className="navbar" aria-label="Main navigation">
      <a href="/" className="logo">Logo</a>
      <ul className="nav-links">
        <li><a href="/">Home</a></li>
        <li><a href="/about">About</a></li>
      </ul>
    </nav>
  );
}

Лучшие практики на 2026 год: используйте новый API use в React 19 вместе с метаданными доступности, а также пропускайте ваши компоненты через инструменты аудита, такие как axe-core или расширение браузера WAVE. Используйте тег <button> для всего, что можно нажать, тег <nav> для блоков навигации, а тег <section> в сочетании с атрибутом aria-labelledby для обозначения регионов страницы.

2. Встроенные стили в JSX (снижение производительности)

Ошибка: определение объектов стилей встроенно в JSX, что приводит к созданию совершенно нового объекта-ссылки при каждой перерисовке.

// ❌ Bad: New object every render
function Badge({ color, children }) {
  return (
    <span style={{ backgroundColor: color, padding: '4px 8px', borderRadius: '4px' }}>
      {children}
    </span>
  );
}

Почему это портит ваше приложение:

  • Дополнительная работа по синхронизации: поскольку при каждой отрисовке создается новый объект стиля, логика сравнения React обнаруживает «изменённое» свойство и влияет на узел DOM, хотя на самом деле ничего не изменилось.
  • Медленнее выполнение CSS-in-JS: библиотеки, которые парсят объявления встроенных стилей, неоднократно платят эту цену, что усугубляет проблему.
  • Уязвимости компилятора: компилятор React 19 способен мемоизировать компоненты, но у него нет способа самостоятельно стабилизировать ссылки на встроенные объекты. Как только он обнаруживает новосозданный объект, он пропускает оптимизацию этой части.
  • Упущенные возможности кэширования: браузеры не могут кэшировать объявления встроенных стилей так же, как кэшируют таблицы стилей. CSS-класс парсится один раз, в то время как встроенные стили перепарсиваются при каждой загрузке компонента.

Решение:

// ✅ Good: CSS classes with dynamic data attributes
function Badge({ color, children }) {
  return (
    <span
      className="badge"
      data-color={color}
      style={{ '--badge-color': color }}
    >
      {children}
    </span>
  );
}
// CSS
// .badge { background-color: var(--badge-color); padding: 4px 8px; border-radius: 4px; }

В качестве альтернативы инструменты Tailwind CSS хорошо справляются с задачами при тщательном использовании с произвольными значениями:

// Acceptable: Tailwind utility classes
function Badge({ color, children }) {
  return (
    <span className="px-2 py-1 rounded" style={{ backgroundColor: color }}>
      {children}
    </span>
  );
}

Лучшая практика 2026 года: использовать статическую стилизацию в модулях CSS, классах Tailwind или подходе CSS-in-JS без выполнения кода во время работы приложения. Оставляйте встроенные стили для действительно динамических значений, таких как ширина полосы прогресса, и даже в этом случае по возможности используйте пользовательские свойства CSS.

3. Неправильное использование свойства key в списках

Ошибка: использование индекса массива в качестве ключа, полное игнорирование свойства key или генерация случайного ключа при каждой отрисовке.

// ❌ Bad: Index as key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo, index) => (
        <li key={index}>{todo.text}</li>
      ))}
    </ul>
  );
}
// ❌ Worse: Random key
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={Math.random()}>{todo.text}</li>
      ))}
    </ul>
  );
}

Почему это ломает ваше приложение:

  • Ключи, основанные на индексе: когда элементы списка перестраиваются, React сопоставляет тот же ключ с той же позицией, в результате чего для неверного элемента используется неверный DOM-узел. Это приводит к смешиванию состояния ввода, фокуса и внутреннего состояния компонентов между элементами.
  • Случайно генерируемые ключи: поскольку при каждой отрисовке генерируется новый ключ, React разрушает и пересоздает каждый DOM-узел при каждом обновлении. Это сильно снижает производительность и уничтожает любое состояние формы в списке.
  • Переходы в React 19: при одновременной отрисовке и переходах некорректные ключи могут вызывать видимые сбои в интерфейсе и повреждение состояния при прерывании обновления.

Решение:

// ✅ Good: Stable, unique identifier
function TodoList({ todos }) {
  return (
    <ul>
      {todos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
}

Если нет доступного естественного уникального идентификатора, создайте его при первой генерации данных, а не во время отображения:

// At data creation, not in the component
const todos = items.map(item => ({
  ...item,
  id: crypto.randomUUID(), // or your backend's ID
}));

Лучшая практика 2026 года: когда список поступает от серверного компонента, убедитесь, что сам сервер предоставляет стабильные идентификаторы. Избегайте формирования ключей на основе позиции в массиве, если только список действительно не является статическим и не расширяется только путем добавления новых элементов.

4. Прямая модификация пропсов

Ошибка: рассматривать пропсы как изменяемые локальные переменные и изменять их внутри дочернего компонента.

// ❌ Bad: Mutating props directly
function SortButton({ items }) {
  const handleSort = () => {
    items.sort((a, b) => a.price - b.price); // MUTATES the prop!
    onSort(items);
  };

  return <button onClick={handleSort}>Sort by Price</button>;
}

Почему это ломает ваше приложение:

  • Незаметное повреждение родительского элемента: состояние родительского элемента изменяется, но вы даже не замечаете этого. Механизм обнаружения изменений в React основан на сравнении ссылок, поэтому такие изменения остаются совершенно незамеченными.
  • Ошибки из-за устаревших данных при перерисовке: поскольку переменная items по-прежнему указывает на тот же массив в памяти, родительский элемент никогда не перерисовывается, в результате чего интерфейс отображает устаревшие данные.
  • Путаница у компилятора React: компилятор предполагает, что пропсы передаются по дереву компонентов без изменений. Прямое изменение пропсов делает бесполезной его логику автоматической мемоизации.
  • Потеря предсказуемости: любой другой компонент, использующий тот же массив, внезапно увидит изменённую, отсортированную версию данных без какого-либо предупреждения.

Решение:

// ✅ Good: Immutable update
function SortButton({ items, onSort }) {
  const handleSort = () => {
    const sorted = [...items].sort((a, b) => a.price - b.price);
    onSort(sorted);
  };

  return <button onClick={handleSort}>Sort by Price</button>;
}

Лучшая практика 2026 года: используйте Immer, когда вложенные мутации кажутся неизбежными, и настройте правило ESLint вроде prefer-readonly-props, если вы работаете с TypeScript. Считайте, что каждый свойство является readonly, если не доказано обратное.

5. Слишком много свойств (вместо композиции)

Ошибка: создание обширных «богатых компонентов», которые принимают десятки свойств лишь для контроля всех возможных внутренних вариаций.

// ❌ Bad: Prop explosion
function Card({
  title,
  subtitle,
  imageUrl,
  imageAlt,
  showImage,
  imagePosition,
  actions,
  showActions,
  actionAlignment,
  badge,
  badgeColor,
  onBadgeClick,
  footer,
  showFooter,
  footerContent,
  borderRadius,
  shadow,
  padding,
  variant,
}) {
  return (
    <div className={`card card--${variant}`} style={{ borderRadius, padding, boxShadow: shadow }}>
      {showImage && (
        <div className={`card__image card__image--${imagePosition}`}>
          <img src={imageUrl} alt={imageAlt} />
        </div>
      )}
      <div className="card__content">
        <h2>{title}</h2>
        {subtitle && <p>{subtitle}</p>}
      </div>
      {/* ... more conditional rendering ... */}
    </div>
  );
}

Почему это разрушает ваше приложение:

  • Хрупкий интерфейс API: Каждое новое требование означает добавление ещё одного свойства, изменение внутренней структуры компонента и риск того, что что-то ещё сломается.
  • Загруженные пакеты: Компонент вынужден импортировать и обрабатывать код для функций, которые на самом деле никогда не нужны в конкретном случае использования.
  • Проблематичное покрытие тестами: Вам приходится проверять всё больше комбинаций свойств.
  • Противоречия с React Server Components: Чрезмерно большие списки свойств заставляют переносить больше логики на клиентскую сторону, чем это действительно необходимо.

Решение:

// ✅ Good: Composition
function Card({ children, className }) {
  return (
    <div className={clsx('card', className)}>
      {children}
    </div>
  );
}
function CardImage({ src, alt, position = 'top' }) {
  return (
    <div className={`card__image card__image--${position}`}>
      <img src={src} alt={alt} loading="lazy" />
    </div>
  );
}
function CardContent({ title, subtitle }) {
  return (
    <div className="card__content">
      <h2>{title}</h2>
      {subtitle && <p>{subtitle}</p>}
    </div>
  );
}
function CardActions({ children, align = 'end' }) {
  return (
    <div className={`card__actions card__actions--${align}`}>
      {children}
    </div>
  );
}
// Usage
<Card>
  <CardImage src="/photo.jpg" alt="Product" />
  <CardContent title="Headphones" subtitle="Noise cancelling" />
  <CardActions>
    <button>Add to Cart</button>
  </CardActions>
</Card>

Лучшая практика на 2026 год: вместо этого используйте шаблон компонента с композицией или композицию на основе слотов. Пусть потребитель компонента сам определяет структуру и содержимое, а сам компонент должен лишь предоставлять контейнер.

6. Неиспользование React.Fragment (ненужные Div-ы)

Ошибка: оборачивание элементов-братьев в обычный <div> исключительно для соблюдения правила JSX, согласно которому компонент должен возвращать единственный корневой узел.

// ❌ Bad: Wrapper div for layout that breaks semantics
function TableCell({ label, value }) {
  return (
    <div> {/* This div breaks the <table> structure! */}
      <td>{label}</td>
      <td>{value}</td>
    </div>
  );
}// ❌ Bad: Extra div in a list
function ListItems({ items }) {
  return (
    <ul>
      {items.map(item => (
        <div key={item.id}> {/* Invalid HTML: div inside ul */}
          <li>{item.name}</li>
          <li>{item.description}</li>
        </div>
      ))}
    </ul>
  );
}

Почему это ломает ваше приложение:

  • Недопустимая разметка: элемент <div> не может быть дочерним элементом таких элементов, как <table>, <ul>, <select> или <dl>. Браузеры молча перепишут DOM, чтобы исправить это, что приводит к несоответствиям во время гидратации при серверной отрисовке.
  • Нарушение лейаута: дополнительные обертывающие элементы мешают работе CSS Grid, Flexbox и свойства gap.
  • Сбои гидратации в React: учитывая распространенность технологий потоковой серверной отрисовки и серверных компонентов, подобное недопустимое вложение может вызвать фатальные несоответствия при гидратации, что приводит к сбою приложения на стороне клиента.

Решение:

// ✅ Good: React.Fragment
function TableCell({ label, value }) {
  return (
    <>
      <td>{label}</td>
      <td>{value}</td>
    </>
  );
}
// ✅ Good: Fragment with key (required in lists)
function ListItems({ items }) {
  return (
    <ul>
      {items.map(item => (
        <React.Fragment key={item.id}>
          <li>{item.name}</li>
          <li>{item.description}</li>
        </React.Fragment>
      ))}
    </ul>
  );
}

Лучшая практика на 2026 год: используйте сокращение <> </>, когда ключ не требуется, а при отрисовке массивов фрагментов, требующих ключа, обращайтесь к <React.Fragment key={...}>. Регулярно проверяйте дерево компонентов в React DevTools, чтобы обнаруживать и удалять ненужные обертывающие div-ы.

7. Отрисовка ложно-числовых значений с помощью && (скрытые ошибки)

Ошибка: использование && для условной отрисовки элементов без учета случаев, когда левый операнд принимает значение 0, NaN или пустую строку.

// ❌ Bad: Shows "0" on screen when items.length is 0
function Cart({ items }) {
  return (
    <div>
      <h1>Shopping Cart</h1>
      {items.length && (
        <ul>
          {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
      )}
    </div>
  );
}
// ❌ Bad: accountBalance could be 0
function AccountSummary({ account }) {
  return (
    <div>
      {account.balance && <p>Balance: ${account.balance}</p>}
    </div>
  );
}

Почему это ломает ваше приложение:

  • React на самом деле отображает 0: запись {0 && <Component />} не приводит к отсутствию отображения контента, а выводит в качестве текста собственно число 0.
  • Невидимые дефекты интерфейса: пользователи вынуждены смотреть на случайное «0» где-то на странице, что создаёт визуальный бардак и может нарушить структуру интерфейса.
  • Значения нуля в реальном мире: такие показатели, как остаток средств на счёте, количество элементов или баллы, часто равны нулю в рамках нормальных условий. Если рассматривать 0 как ложное значение с помощью &&, это приведёт к тому, что либо ничего не будет отображаться там, где это не нужно, либо будет отображаться само число там, где этого не желательно.

Решение:

// ✅ Good: Explicit boolean check
function Cart({ items }) {
  return (
    <div>
      <h1>Shopping Cart</h1>
      {items.length > 0 && (
        <ul>
          {items.map(item => <li key={item.id}>{item.name}</li>)}
        </ul>
      )}
    </div>
  );
}
// ✅ Good: Nullish coalescing for defaults, ternary for conditionals
function AccountSummary({ account }) {
  return (
    <div>
      {account.balance != null ? (
        <p>Balance: ${account.balance}</p>
      ) : (
        <p>Loading balance...</p>
      )}
    </div>
  );
}

Лучшая практика на 2026 год: полностью избегайте использования && для условной отрисовки. Лучше использовать тернарный оператор (condition ? : null) или явно задать логическое значение с помощью чего-то вроде Boolean(count) && .... Добавьте в ваш проект правило ESLint, которое будет выделять рискованные варианты использования && в JSX.

8. Игнорирование мемоизации для дорогостоящего JSX

Ошибка: позволять дорогостоящему поддереву перерисовываться при каждом обновлении родителя, даже если соответствующие свойства на самом деле не изменились.

// ❌ Bad: HeavyChart re-renders every time Parent re-renders
function Dashboard({ data, filter }) {
  const [hoveredItem, setHoveredItem] = useState(null);

  return (
    <div>
      <Sidebar onHover={setHoveredItem} />
      <HeavyChart data={data} filter={filter} /> {/* Re-renders on every hover! */}
      <Tooltip item={hoveredItem} />
    </div>
  );
}
function HeavyChart({ data, filter }) {
  // Expensive SVG rendering, D3 calculations, etc.
  return <svg>{/* thousands of nodes */}</svg>;
}

Почему это портит ваше приложение:

  • Потеря вычислительных ресурсов: HeavyChart повторно выполняет свою логику отрисовки даже тогда, когда data и filter остаются неизменными.
  • Падение частоты кадров: если отрисовка HeavyChart занимает примерно 50 мс, простое событие наведения в боковой панели может снизить частоту кадров до примерно 20 кадров в секунду.
  • Ограничения компилятора React: автоматическая мемоизация, предоставляемая компилятором, работает внутри компонента, но не может оптимизировать работу между компонентами, если родительский компонент постоянно передаёт новые ссылки на функции-обратные вызовы.

Решение:

// ✅ Good: Memoize the expensive component
const HeavyChart = memo(function HeavyChart({ data, filter }) {
  // Expensive rendering
  return <svg>{/* thousands of nodes */}</svg>;
});
function Dashboard({ data, filter }) {
  const [hoveredItem, setHoveredItem] = useState(null);

  // Stabilize the callback if passed to children
  const handleHover = useCallback((item) => setHoveredItem(item), []);

  return (
    <div>
      <Sidebar onHover={handleHover} />
      <HeavyChart data={data} filter={filter} />
      <Tooltip item={hoveredItem} />
    </div>
  );
}

Лучшая практика 2026 года: компилятор React 19 сокращает необходимость вручную реализовывать мемоизацию в простых случаях, но для действительно объемных поддеревьев использование тега memo по-прежнему целесообразно. Используйте профилятор React DevTools, чтобы определить места настоящих узких мест перед тем, как прибегать к мемоизации.

9. Сочетание представления и бизнес-логики

Ошибка: создание одного компонента, который одновременно загружает данные, преобразует их, отслеживает состояние формы, выполняет побочные эффекты и отрисовывает маркировку.

// ❌ Bad: God component
function UserProfile({ userId }) {
  const [user, setUser] = useState(null);
  const [loading, setLoading] = useState(true);
  const [isEditing, setIsEditing] = useState(false);
  const [formData, setFormData] = useState({});

  useEffect(() => {
    fetchUser(userId).then(data => {
      setUser(data);
      setFormData(data);
      setLoading(false);
    });
  }, [userId]);

  const handleSubmit = async () => {
    await updateUser(userId, formData);
    setIsEditing(false);
  };

  if (loading) return <Spinner />;

  return (
    <div>
      <h1>{user.name}</h1>
      {isEditing ? (
        <form onSubmit={handleSubmit}>
          <input
            value={formData.name}
            onChange={e => setFormData({...formData, name: e.target.value})}
          />
        </form>
      ) : (
        <button onClick={() => setIsEditing(true)}>Edit</button>
      )}
    </div>
  );
}

Почему это портит ваше приложение:

  • Трудно тестировать: невозможно проверить логику загрузки данных без использования всего компонента, а проверить отрисованное интерфейсное оформление нельзя без имитации сетевого слоя.
  • Невозможно повторное использование: поведение формы заключено внутри этого одного компонента и не может быть использовано в других местах.
  • Сложно понимать структуру: состояние, побочные эффекты и маркировка переплетены между собой, поэтому небольшая правка интерфейса может случайно нарушить логику обработки данных.
  • Несоответствие стандартам серверных компонентов: согласно стандартам на 2026 год загрузка данных должна происходить в серверных компонентах, тогда как клиентские компоненты предназначены исключительно для обеспечения интерактивности.

Решение:

// ✅ Good: Separated concerns
// Container / Logic Hook
function useUserProfile(userId) {
  const { data: user, isLoading } = useQuery({
    queryKey: ['user', userId],
    queryFn: () => fetchUser(userId),
  });

  const updateMutation = useMutation({
    mutationFn: updateUser,
    onSuccess: () => queryClient.invalidateQueries({ queryKey: ['user', userId] }),
  });

  return { user, isLoading, updateUser: updateMutation.mutate };
}
// UI Component
function UserProfile({ userId }) {
  const { user, isLoading, updateUser } = useUserProfile(userId);
  const [isEditing, setIsEditing] = useState(false);

  if (isLoading) return <Spinner />;

  return (
    <div>
      <h1>{user.name}</h1>
      {isEditing ? (
        <UserProfileForm
          initialData={user}
          onSubmit={(data) => {
            updateUser({ userId, ...data });
            setIsEditing(false);
          }}
          onCancel={() => setIsEditing(false)}
        />
      ) : (
        <button onClick={() => setIsEditing(true)}>Edit</button>
      )}
    </div>
  );
}
// Reusable Form Component
function UserProfileForm({ initialData, onSubmit, onCancel }) {
  const [formData, setFormData] = useState(initialData);

  return (
    <form onSubmit={(e) => { e.preventDefault(); onSubmit(formData); }}>
      <input
        value={formData.name}
        onChange={e => setFormData(prev => ({...prev, name: e.target.value}))}
      />
      <button type="submit">Save</button>
      <button type="button" onClick={onCancel}>Cancel</button>
    </form>
  );
}

Рекомендуемый подход на 2026 год: разделение обязанностей на пользовательские хуки для логики, простые компоненты для отображения контента и серверные компоненты для загрузки данных. Компонент должен либо выполнять какое-либо действие, либо отображать информацию; редко бывает, чтобы он хорошо справлялся с обеими задачами одновременно.

10. Неиспользование интерфейсов TypeScript для параметров компонентов

Ошибка: использование файла prop-types, разрозненных комментариев JSDoc или, в худшем случае, полное игнорирование проверки типов параметров.

// ❌ Bad: No types
function Button(props) {
  return <button className={props.variant}>{props.children}</button>;
}
// ❌ Bad: PropTypes at runtime only
import PropTypes from 'prop-types';
function Button(props) {
  return <button className={props.variant}>{props.children}</button>;
}
Button.propTypes = {
  variant: PropTypes.string,
  children: PropTypes.node,
};

Почему это разрушает ваше приложение:

  • Незаметные сбои: опечатки в именах свойств, отсутствие необходимых значений и несоответствие типов приводят к сбоям во время выполнения, вместо того чтобы быть обнаруженными до развертывания.
  • Рефакторинг становится сложным: переименование одного свойства заставляет вас вручную искать его по всему кодовому базису, тогда как система типов сразу же выделяет все места вызова, которые требуют обновления.
  • Ухудшение опыта разработчика: без типов теряются автодополнение, подсказки встроенной документации и защитные механизмы для автоматического переименования.
  • Недостаточная совместимость с React 19: новые возможности React, такие как серверные компоненты, использование use с Promises и пересылка ссылок, разрабатываются с учетом модели типов TypeScript, поэтому отказ от типов означает потерю гарантий компиляции, на которых основаны эти функции.

Решение:

// ✅ Good: Explicit TypeScript interface
interface ButtonProps {
  variant?: 'primary' | 'secondary' | 'danger';
  size?: 'sm' | 'md' | 'lg';
  disabled?: boolean;
  onClick?: (event: React.MouseEvent<HTMLButtonElement>) => void;
  children: React.ReactNode;
  type?: 'button' | 'submit' | 'reset';
}
function Button({
  variant = 'primary',
  size = 'md',
  disabled = false,
  onClick,
  children,
  type = 'button'
}: ButtonProps) {
  return (
    <button
      type={type}
      className={`btn btn--${variant} btn--${size}`}
      disabled={disabled}
      onClick={onClick}
    >
      {children}
    </button>
  );
}

Рекомендуемый подход на 2026 год: включите строгий режим (strict: true в файле tsconfig.json). Определяйте свойства моделей с помощью деклараций interface, а не алиасов type, поскольку интерфейсы обычно генерируют более четкие сообщения об ошибках и их легче расширять. Используйте React.ComponentProps, когда необходимо получать типы из стандартных элементов HTML при создании оберточных компонентов. Ни в коем случае не используйте any для типов свойств.

Краткое резюме: чек-лист компонентов на 2026 год

Перед слиянием компонента пройдитесь по этому чек-листу:

  • [ ] Вы выбрали семантический HTML вместо накопления обычных элементов <div>?
  • [ ] Стили определены в CSS, а не в объектах встроенных стилей?
  • [ ] Пункты списка используют стабильные, уникальные значения key?
  • [ ] Рассматриваются ли все пропсы всегда как только для чтения?
  • [ ] Можно ли составить компонент вместо того, чтобы использовать более десяти параметров конфигурации?
  • [ ] Заменили ли вы ненужные обертывающие div-ы на Fragment?
  • [ ] Выполняется ли условная отрисовка с использованием тернарных операторов или явных проверок на логические значения вместо &&?
  • [ ] Мемоизируется ли дорогостоящий JSX там, где это важно?
  • [ ] Разделена ли бизнес-логика от маркипада, находясь ли она в хуках или вспомогательных функциях?
  • [ ] Описаны ли все пропсы полностью с помощью интерфейсов TypeScript?

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