Оптимизация рендеринга

В Chakra UI каждый компонент построен с учетом оптимизации рендера, однако, как и в любом React-проекте, неконтролируемые ререндеры могут снижать производительность. Основной принцип — минимизировать количество обновлений, которые не влияют на визуальную часть интерфейса.

Ключевые подходы:

  • Использование React.memo для обертки компонентов, которые не зависят от часто изменяющихся пропсов. Это предотвращает повторный рендер при неизменных свойствах.
  • Разделение сложных компонентов на более мелкие. Мелкие компоненты проще кэшировать и реиспользовать.
  • Избегание создания новых объектов или функций внутри рендера без необходимости, поскольку это заставляет дочерние компоненты пересчитывать свои пропсы.
import { Box, Button } from "@chakra-ui/react";
import React, { memo } from "react";

const MemoizedButton = memo(({ onClick, label }) => {
  console.log("Rendering Button:", label);
  return <Button onCl ick={onClick}>{label}</Button>;
});

export default function App() {
  return (
    <Box>
      <MemoizedButton label="Click me" onCl ick={() => console.log("Clicked")} />
    </Box>
  );
}

Использование shouldForwardProp и пропсов стилей

Chakra UI позволяет создавать стилизованные компоненты через styled API. Часто ненужные пропсы передаются внутрь DOM, вызывая лишние ререндеры и предупреждения в консоли.

Оптимизация через shouldForwardProp:

  • Фильтрует пропсы, которые не должны передаваться в DOM.
  • Уменьшает нагрузку на виртуальный DOM и предотвращает перерендеры ненужных узлов.
import { chakra, shouldForwardProp } from "@chakra-ui/react";
import { isPropValid } from "@emotion/is-prop-valid";

const CustomBox = chakra("div", {
  shouldForwardProp: (prop) => isPropValid(prop) && prop !== "customProp"
});

export default function Example() {
  return <CustomBox customProp="value">Контент</CustomBox>;
}

Ленивая загрузка компонентов (lazy и Suspense)

Для больших приложений важно уменьшить время первичной загрузки страницы. Chakra UI легко интегрируется с механизмами React для ленивой загрузки.

Принцип работы:

  • Компонент загружается только при первом рендере.
  • Сокращает объем исходного бандла.
  • Совместим с Chakra UI, если использовать внутри обертки Suspense.
import React, { lazy, Suspense } from "react";

const LazyModal = lazy(() => import("./LazyModal"));

export default function App() {
  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <LazyModal />
    </Suspense>
  );
}

Контроль состояния с useDisclosure и useBoolean

Chakra UI предоставляет хелперы для управления состоянием компонентов. Их правильное использование уменьшает количество локальных стейтов и связанных ререндеров.

Примеры:

  • useDisclosure — для модальных окон, аккордеонов и тултипов.
  • useBoolean — для булевых флагов, особенно при переключении видимости элементов.
import { Button, useDisclosure, Modal, ModalOverlay, ModalContent } from "@chakra-ui/react";

export default function Example() {
  const { isOpen, onOpen, onClose } = useDisclosure();

  return (
    <>
      <Button onCl ick={onOpen}>Открыть модал</Button>
      <Modal isOpen={isOpen} onCl ose={onClose}>
        <ModalOverlay />
        <ModalContent>Содержимое модала</ModalContent>
      </Modal>
    </>
  );
}

Эти хуки гарантируют, что изменения состояния ограничиваются только связанными компонентами, минимизируя ненужные обновления всей страницы.


Анимации и ререндеры

Анимации могут быть источником значительных затрат ресурсов. Chakra UI использует библиотеку framer-motion для анимаций, что позволяет:

  • Управлять анимациями через props motion без постоянного пересчета стейтов.
  • Использовать AnimatePresence для управления монтированием и размонтированием элементов.
import { Box, Button } from "@chakra-ui/react";
import { motion, AnimatePresence } from "framer-motion";
import React, { useState } from "react";

const MotionBox = motion(Box);

export default function Example() {
  const [isVisible, setVisible] = useState(false);

  return (
    <>
      <Button onCl ick={() => setVisible(!isVisible)}>Toggle</Button>
      <AnimatePresence>
        {isVisible && (
          <MotionBox
            initial={{ opacity: 0 }}
            animate={{ opacity: 1 }}
            exit={{ opacity: 0 }}
          >
            Анимированный блок
          </MotionBox>
        )}
      </AnimatePresence>
    </>
  );
}

Правильное использование AnimatePresence предотвращает лишние ререндеры при скрытии элементов и делает анимации более эффективными.


Оптимизация темы и стилей

Частое пересоздание объекта темы вызывает глубокие ререндеры всех дочерних компонентов.

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

  • Создавать тему один раз при инициализации приложения.
  • Использовать extendTheme только один раз и передавать через ChakraProvider.
  • Для динамических стилей использовать sx или CSS-переменные вместо создания новых объектов темы.
import { ChakraProvider, extendTheme, Box } from "@chakra-ui/react";

const theme = extendTheme({
  colors: {
    brand: { 500: "#1a365d" }
  }
});

export default function App() {
  return (
    <ChakraProvider theme={theme}>
      <Box bg="brand.500" w="100px" h="100px"></Box>
    </ChakraProvider>
  );
}

Эти методы позволяют сократить количество ререндеров и обеспечивают стабильность производительности при больших проектах.


Выводы по оптимизации рендеринга

Основная идея — минимизация лишних пересчетов и ререндеров за счет:

  • мемоизации компонентов,
  • фильтрации пропсов,
  • контроля состояния через специализированные хуки,
  • ленивой загрузки тяжелых компонентов,
  • правильного управления анимациями и темой.

Следование этим практикам позволяет Chakra UI работать максимально эффективно даже в сложных интерфейсах с большим количеством динамических элементов.