Запобігання непотрібним повторним рендерам у React за допомогою Tailwind CSS

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

  1. Створіть компонент спостерігача прокрутки
"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;  
}  
  1. Застосуйте group та data-scroll в Layout
export default function Layout({ children }) {  
  return (  
    <div data-scroll="false">  
      {children}  
    </div>  
  );  
}  
  1. Стилізуйте за допомогою модифікаторів атрибутів 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

  1. Простота коду: Використовуючи утилітні класи Tailwind CSS, можна уникнути додавання стану та обробників подій, що призводить до більш чистого та зручного для підтримки коду.
  2. Покращена продуктивність: Зменшення кількості повторних рендерів може покращити продуктивність, особливо в складних додатках з великою кількістю компонентів.
  3. Послідовність стилів: 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

Leave a Reply

Your email address will not be published. Required fields are marked *