perf(supplies): optimize React components with memo and callbacks

ФАЗА 2: Оптимизация производительности завершена:
- Обернуты все блок-компоненты в React.memo для предотвращения лишних ререндеров
- Добавлены useCallback для всех обработчиков событий в главном компоненте
- Оптимизированы зависимости для минимизации пересоздания функций
- Страница остается полностью функциональной

Компоненты с memo: SuppliersBlock, ProductCardsBlock, DetailedCatalogBlock, CartBlock
Callbacks: handleSupplierSelect, handleProductAdd, handleQuantityChange, handleRecipeChange

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Veronika Smirnova
2025-08-12 20:40:57 +03:00
parent 94ea6c2c77
commit 6d7762b2ee
6 changed files with 66 additions and 39 deletions

View File

@ -8,13 +8,14 @@
'use client'
import { ShoppingCart, X } from 'lucide-react'
import React from 'react'
import { Button } from '@/components/ui/button'
import { DatePicker } from '@/components/ui/date-picker'
import type { CartBlockProps } from '../types/supply-creation.types'
export function CartBlock({
export const CartBlock = React.memo(function CartBlock({
selectedGoods,
selectedSupplier,
deliveryDate,
@ -155,4 +156,4 @@ export function CartBlock({
</div>
</div>
)
}
})

View File

@ -9,6 +9,7 @@
import { Package, Settings, Building2 } from 'lucide-react'
import Image from 'next/image'
import React from 'react'
import { Badge } from '@/components/ui/badge'
import { DatePicker } from '@/components/ui/date-picker'
@ -24,7 +25,7 @@ import type {
SellerConsumable,
} from '../types/supply-creation.types'
export function DetailedCatalogBlock({
export const DetailedCatalogBlock = React.memo(function DetailedCatalogBlock({
allSelectedProducts,
productRecipes,
fulfillmentServices,
@ -129,7 +130,7 @@ export function DetailedCatalogBlock({
</div>
</div>
)
}
})
// Компонент детальной карточки товара с рецептурой
interface ProductDetailCardProps {

View File

@ -9,12 +9,17 @@
import { Package, Plus } from 'lucide-react'
import Image from 'next/image'
import React from 'react'
import { Badge } from '@/components/ui/badge'
import type { ProductCardsBlockProps } from '../types/supply-creation.types'
export function ProductCardsBlock({ products, selectedSupplier, onProductAdd }: ProductCardsBlockProps) {
export const ProductCardsBlock = React.memo(function ProductCardsBlock({
products,
selectedSupplier,
onProductAdd,
}: ProductCardsBlockProps) {
if (!selectedSupplier) {
return (
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6">
@ -141,4 +146,4 @@ export function ProductCardsBlock({ products, selectedSupplier, onProductAdd }:
</div>
</div>
)
}
})

View File

@ -8,13 +8,14 @@
'use client'
import { Search } from 'lucide-react'
import React from 'react'
import { OrganizationAvatar } from '@/components/market/organization-avatar'
import { Input } from '@/components/ui/input'
import type { SuppliersBlockProps } from '../types/supply-creation.types'
export function SuppliersBlock({
export const SuppliersBlock = React.memo(function SuppliersBlock({
suppliers,
selectedSupplier,
searchQuery,
@ -132,7 +133,7 @@ export function SuppliersBlock({
)}
</div>
)
}
})
// Утилитарная функция для меток рынков (временно, потом перенести в хук)
function getMarketLabel(market?: string) {

View File

@ -9,6 +9,7 @@
import { ArrowLeft } from 'lucide-react'
import { useRouter } from 'next/navigation'
import React, { useCallback } from 'react'
import { Sidebar } from '@/components/dashboard/sidebar'
import { Button } from '@/components/ui/button'
@ -89,41 +90,53 @@ export function CreateSuppliersSupplyPage() {
} = useRecipeBuilder({ selectedFulfillment })
// Обработчики событий для блоков
const handleSupplierSelect = (supplier: GoodsSupplier) => {
setSelectedSupplier(supplier)
// Сбрасываем выбранные товары при смене поставщика
setAllSelectedProducts([])
setSelectedGoods([])
}
const handleSupplierSelect = useCallback(
(supplier: GoodsSupplier) => {
setSelectedSupplier(supplier)
// Сбрасываем выбранные товары при смене поставщика
setAllSelectedProducts([])
setSelectedGoods([])
},
[setSelectedSupplier, setAllSelectedProducts, setSelectedGoods],
)
const handleProductAdd = (product: GoodsProduct) => {
const quantity = getProductQuantity(product.id) || 1
addProductToSelected(product, quantity)
initializeProductRecipe(product.id)
const handleProductAdd = useCallback(
(product: GoodsProduct) => {
const quantity = getProductQuantity(product.id) || 1
addProductToSelected(product, quantity)
initializeProductRecipe(product.id)
// Добавляем в корзину
addToCart(product, quantity)
}
const handleQuantityChange = (productId: string, quantity: number) => {
updateSelectedProductQuantity(productId, quantity)
// Синхронизируем с корзиной
const product = allSelectedProducts.find((p) => p.id === productId)
if (product && quantity > 0) {
// Добавляем в корзину
addToCart(product, quantity)
} else if (quantity === 0) {
removeFromCart(productId)
removeProductFromSelected(productId)
}
}
},
[getProductQuantity, addProductToSelected, initializeProductRecipe, addToCart],
)
const handleRecipeChange = (productId: string, recipe: ProductRecipe) => {
setProductRecipes((prev) => ({
...prev,
[productId]: recipe,
}))
}
const handleQuantityChange = useCallback(
(productId: string, quantity: number) => {
updateSelectedProductQuantity(productId, quantity)
// Синхронизируем с корзиной
const product = allSelectedProducts.find((p) => p.id === productId)
if (product && quantity > 0) {
addToCart(product, quantity)
} else if (quantity === 0) {
removeFromCart(productId)
removeProductFromSelected(productId)
}
},
[updateSelectedProductQuantity, allSelectedProducts, addToCart, removeFromCart, removeProductFromSelected],
)
const handleRecipeChange = useCallback(
(productId: string, recipe: ProductRecipe) => {
setProductRecipes((prev) => ({
...prev,
[productId]: recipe,
}))
},
[setProductRecipes],
)
// Обработчик ошибок
if (suppliersError) {