React — потужна бібліотека для створення користувацьких інтерфейсів, але однією з поширених проблем, з якими стикаються розробники, є непотрібні повторні рендери. Це може погіршити продуктивність, особливо в великих додатках. Tailwind CSS, CSS-фреймворк з підходом utility-first, можна використовувати для мінімізації цих повторних рендерів. У цій статті ми розглянемо, як використовувати модифікатор group
в Tailwind CSS для запобігання непотрібним повторним рендерам у компонентах React.
Розуміння проблеми
У React компонент повторно рендериться, коли змінюється його стан або властивості. Однак іноді компоненти рендеряться знову, навіть якщо в інтерфейсі немає видимих змін. Це може трапитися через:
- Повторний рендер батьківського компонента: коли батьківський компонент рендериться знову, всі його дочірні компоненти рендеряться за замовчуванням.
- Зміни в контексті: якщо компонент використовує контекст, він буде рендеритися щоразу, коли значення контексту змінюється, навіть якщо ці зміни не мають відношення до компонента.
- Інлайн-функції та об'єкти: передача інлайн-функцій або об'єктів як властивостей може спричиняти повторні рендери, оскільки вони пересоздаються при кожному рендері.
- Недотримання кращих практик для властивості
key
: коли рендеряться списки компонентів, стилізованих за допомогою Tailwind (наприклад, сітки/картки), неправильне використання властивостіkey
(наприклад, індекси або нестабільні значення) може спричиняти непотрібні повторні рендери або збої в інтерфейсі. У статті можливо не підкреслено важливість унікальних ключів.
Хоча React React.memo
та useMemo
можуть допомогти зменшити ці проблеми, вони додають складності коду. Tailwind CSS пропонує простіше і елегантніше рішення для певних випадків.
Непорозуміння щодо ролі Tailwind CSS в повторних рендерах
- Сам по собі Tailwind CSS не спричиняє повторні рендери, але динамічне створення класів (наприклад, комбінування класів умовно) може призвести до непотрібних повторних рендерів, якщо їх не мемоізувати. Наприклад:
// Проблемний приклад: Динамічний рядок класу пересоздається при кожному рендері
const Button = ({ isActive }) => (
<button className={`p-2 ${isActive ? 'bg-blue-500' : 'bg-gray-500'}`}>
Click me
</button>
);
Модифікатор group
в Tailwind CSS
Tailwind CSS надає модифікатор group
, який дозволяє стилізувати дочірні елементи в залежності від стану батьківського елемента. Це особливо корисно для станів hover, focus та active. Використовуючи модифікатор group
, можна уникнути додавання стану до дочірніх компонентів, тим самим запобігаючи непотрібним повторним рендерям.
Приклад: Ефект при наведенні без стану
Розглянемо ситуацію, коли потрібно змінити колір дочірнього елемента, коли на батьківський наводиться курсор. Без Tailwind CSS можна використовувати стан для досягнення цього:
import React, { useState } from 'react';
const HoverComponent = () => {
const [isHovered, setIsHovered] = useState(false);
return (
<div
onMouseEnter={() => setIsHovered(true)}
onMouseLeave={() => setIsHovered(false)}
className="p-4 bg-gray-200"
>
{isHovered ? 'Hovered!' : 'Hover over me!'}
</div>
);
};
export default HoverComponent;
У цьому прикладі компонент HoverComponent
рендериться знову кожного разу, коли миша входить або виходить з батьківського елемента div
. Це непотрібно, тому що зміни в інтерфейсі є лише презентаційними.
Використання модифікатора group
в Tailwind CSS
З Tailwind CSS ви можете досягти того ж ефекту без використання стану:
import React from 'react';
const HoverComponent = () => {
return (
<div className="group p-4 bg-gray-200">
<p className="group-hover:text-blue-500">Hover over me!</p>
</div>
);
};
export default HoverComponent;
У цій версії модифікатор group
застосовується до батьківського елемента div
, а префікс group-hover:
використовується для стилізації дочірнього елемента p
, коли на батьківський елемент наводиться курсор. Такий підхід усуває необхідність в стані, запобігаючи непотрібним повторним рендером.
Приклад: Вкладені групи
Модифікатор group
також можна використовувати у вкладених структурах.
Наприклад, можна мати кілька груп у одному компоненті, кожна з яких має власні ефекти при наведенні:
import React from 'react';
const NestedGroupComponent = () => {
return (
<div className="group p-4 bg-gray-200">
<p className="group-hover:text-blue-500">Hover over me!</p>
<div className="group group-hover:bg-green-200 p-4">
Nested hover effect
</div>
</div>
);
};
export default NestedGroupComponent;
У цьому прикладі зовнішня group
впливає на перший елемент p
, а внутрішня group
впливає на другий елемент p
. Це дозволяє створювати складні взаємодії при наведенні без додавання непотрібного стану або повторних рендерів.
Модифікатори group
в Tailwind та HTML-атрибути data-*
Модифікатори group
в Tailwind дозволяють стилізувати дочірні елементи на основі стану батьківського елемента (як, наприклад, стан hover або положення прокрутки). Поєднуючи це з HTML-атрибутами data-*
, можна перемикати стилі без використання стану React — повністю усуваючи повторні рендери.
Приклад: Липка панель навігації з ефектами на основі прокрутки
Перейдемо до реалізації панелі навігації, яка змінює свій фон при прокручуванні.
Традиційний підхід у React
function StickyNav() {
const [isScrolled, setIsScrolled] = useState(false);
useEffect(() => {
const handleScroll = () => setIsScrolled(window.scrollY > 0);
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return (
{/* ... */}
);
}
Тут isScrolled
викликає повторний рендер при кожній події прокрутки.
Підхід Tailwind + Data Attributes
- Створіть компонент спостерігача прокрутки
"use client";
import { useEffect } from "react";
export default function ScrollObserver() {
useEffect(() => {
let isScrolled = false;
const handleScroll = () => {
const shouldBeScrolled = window.scrollY > 0;
if (isScrolled !== shouldBeScrolled) {
isScrolled = shouldBeScrolled;
document.body.setAttribute("data-scroll", String(shouldBeScrolled));
}
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
return null;
}
- Застосуйте
group
таdata-scroll
в Layout
export default function Layout({ children }) {
return (
<div data-scroll="false">
{children}
</div>
);
}
- Стилізуйте за допомогою модифікаторів атрибутів Tailwind
function StickyNav() {
return (
<nav className="bg-white group" data-scroll="true">
{/* ... */}
</nav>
);
}
Результат: Стилі панелі навігації оновлюються при прокрутці без повторних рендерів у React.
Розширене використання та переваги
1. Динамічні логотипи
Перемикайте між двома зображеннями в залежності від стану прокрутки:
function Logo() {
return (
<img src="logo.png" className="hidden group-hover:block" />
);
}
Не потрібен стан чи ефекти — чистий CSS керує видимістю.
2.
Уникнення накладних витрат Context API
Замість того, щоб поширювати стан через контекст React, використовуйте атрибути data-*
для перемикання теми:
// ThemeToggle.js
function ThemeToggle() {
const toggleTheme = () => {
document.body.setAttribute("data-theme",
document.body.getAttribute("data-theme") === "dark" ? "light" : "dark"
);
};
return Toggle Theme;
}
// Component.js
function Component() {
return (
{/* ... */}
);
}
Коли не слід використовувати цей підхід
- Логіка, що залежить від стану: Якщо дочірні компоненти потребують стану прокрутки для обчислень (наприклад, аналітика), краще використовувати стан React.
- Складні взаємодії: Вкладені модифікатори
group
можуть викликати конфлікти; використовуйте іменовані групи (наприклад,group/name
) для кращої зрозумілості.
Переваги використання модифікатора group
в Tailwind CSS
- Простота коду: Використовуючи утилітні класи Tailwind CSS, можна уникнути додавання стану та обробників подій, що призводить до більш чистого та зручного для підтримки коду.
- Покращена продуктивність: Зменшення кількості повторних рендерів може покращити продуктивність, особливо в складних додатках з великою кількістю компонентів.
- Послідовність стилів: Tailwind CSS забезпечує узгодженість стилів для різних станів, зменшуючи ризик помилок і несумісностей.
Висновок
Модифікатор group
в Tailwind CSS — потужний інструмент для запобігання непотрібним повторним рендерам в компонентах React. Використовуючи утилітні класи для керування станами hover, focus і active, ви можете спростити свій код і покращити продуктивність. Цей підхід особливо корисний для змін, що стосуються лише презентації, які не потребують управління станом.
Наступного разу, коли ви додаватимете стан для обробки простих взаємодій в інтерфейсі, подумайте, чи може модифікатор group
в Tailwind CSS допомогти вам досягти того ж результату ефективніше. Ваш код (і ваші користувачі) будуть вдячні!
Якщо ви знайшли цю статтю корисною, ознайомтесь з іншою статтею Як зменшити рендеринг в React. В моїй статті "Як запобігти повторним рендерам в React" я детальніше розглядаю передові техніки для оптимізації продуктивності React.
https://tailwindcss.com/docs/hover-focus-and-other-states
https://developer.mozilla.org/en-US/docs/Learnwebdevelopment/Howto/SolveHTMLproblems/Usedataattributes
Перекладено з: Preventing Unnecessary Re-renders in React with Tailwind CSS