Для интеграции библиотеки Packery в проект на React сначала необходимо установить её через npm:
npm install packery
npm install imagesloaded
imagesloaded часто используется вместе с Packery для
корректного выравнивания элементов после загрузки изображений. В React
важно помнить, что манипуляция DOM напрямую должна происходить через
refs или эффекты (useEffect), чтобы избежать
конфликтов с виртуальным DOM.
import React, { useEffect, useRef } from "react";
import Packery from "packery";
import imagesLoaded from "imagesloaded";
const Grid = ({ items }) => {
const gridRef = useRef(null);
useEffect(() => {
const packeryInstance = new Packery(gridRef.current, {
itemSelector: ".grid-item",
gutter: 10,
percentPosition: true
});
imagesLoaded(gridRef.current, () => {
packeryInstance.layout();
});
return () => packeryInstance.destroy();
}, [items]);
return (
<div ref={gridRef} className="grid">
{items.map((item, index) => (
<div className="grid-item" key={index}>
{item.content}
</div>
))}
</div>
);
};
export default Grid;
true,
позиционирование будет в процентах, полезно для адаптивных сеток.Для динамического добавления или удаления элементов необходимо
вызвать методы Packery через ref. Основные методы:
Пример добавления нового элемента в React:
const addItem = (newItem) => {
const element = document.createElement("div");
element.className = "grid-item";
element.innerText = newItem.content;
gridRef.current.appendChild(element);
packeryInstance.appended(element);
packeryInstance.layout();
};
Для адаптивной работы сетки важно использовать percentPosition и обрабатывать события изменения размеров окна:
useEffect(() => {
const handleResize = () => {
packeryInstance.layout();
};
window.addEventListener("resize", handleResize);
return () => window.removeEventListener("resize", handleResize);
}, []);
Packery корректно рассчитывает позиции только после того, как все
изображения загружены. Для этого применяется библиотека
imagesloaded:
imagesLoaded(gridRef.current, () => {
packeryInstance.layout();
});
Если изображения загружаются динамически, необходимо повторно
вызывать layout() после их добавления.
Для удобного управления сеткой можно создать кастомный хук:
import { useEffect, useRef } from "react";
import Packery from "packery";
import imagesLoaded from "imagesloaded";
export const usePackery = (items, options = {}) => {
const gridRef = useRef(null);
const packeryRef = useRef(null);
useEffect(() => {
if (!gridRef.current) return;
packeryRef.current = new Packery(gridRef.current, options);
imagesLoaded(gridRef.current, () => {
packeryRef.current.layout();
});
return () => packeryRef.current.destroy();
}, []);
useEffect(() => {
packeryRef.current && packeryRef.current.layout();
}, [items]);
return gridRef;
};
Применение хука в компоненте:
const Grid = ({ items }) => {
const gridRef = usePackery(items, { itemSelector: ".grid-item", gutter: 10 });
return (
<div ref={gridRef} className="grid">
{items.map((item, index) => (
<div className="grid-item" key={index}>
{item.content}
</div>
))}
</div>
);
};
Для корректной работы Packery каждый элемент сетки должен иметь фиксированные размеры или размеры, вычисляемые через CSS. Пример базовой сетки:
.grid {
width: 100%;
position: relative;
}
.grid-item {
width: 30%;
margin-bottom: 10px;
background: #f2f2f2;
padding: 10px;
box-sizing: border-box;
}
Использование процентов для ширины позволяет сетке адаптироваться к размеру контейнера.
Packery совместим с библиотекой Draggabilly для перетаскивания элементов:
import Draggabilly from "draggabilly";
gridRef.current.querySelectorAll(".grid-item").forEach((item) => {
const draggie = new Draggabilly(item);
packeryInstance.bindDraggabillyEvents(draggie);
});
Это позволяет изменять расположение элементов интерактивно и динамически обновлять сетку.
Packery в связке с React обеспечивает гибкую, адаптивную и
интерактивную сетку элементов. Управление через refs,
useEffect и кастомные хуки позволяет сохранять
совместимость с виртуальным DOM и упрощает динамическое добавление или
удаление контента.