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:
6
dev.log
6
dev.log
@ -41,3 +41,9 @@
|
|||||||
POST /api/graphql 200 in 936ms
|
POST /api/graphql 200 in 936ms
|
||||||
POST /api/graphql 200 in 638ms
|
POST /api/graphql 200 in 638ms
|
||||||
POST /api/graphql 200 in 489ms
|
POST /api/graphql 200 in 489ms
|
||||||
|
POST /api/graphql 200 in 560ms
|
||||||
|
POST /api/graphql 200 in 473ms
|
||||||
|
POST /api/graphql 200 in 1273ms
|
||||||
|
POST /api/graphql 200 in 1323ms
|
||||||
|
POST /api/graphql 200 in 475ms
|
||||||
|
POST /api/graphql 200 in 907ms
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { ShoppingCart, X } from 'lucide-react'
|
import { ShoppingCart, X } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
import { DatePicker } from '@/components/ui/date-picker'
|
import { DatePicker } from '@/components/ui/date-picker'
|
||||||
|
|
||||||
import type { CartBlockProps } from '../types/supply-creation.types'
|
import type { CartBlockProps } from '../types/supply-creation.types'
|
||||||
|
|
||||||
export function CartBlock({
|
export const CartBlock = React.memo(function CartBlock({
|
||||||
selectedGoods,
|
selectedGoods,
|
||||||
selectedSupplier,
|
selectedSupplier,
|
||||||
deliveryDate,
|
deliveryDate,
|
||||||
@ -155,4 +156,4 @@ export function CartBlock({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import { Package, Settings, Building2 } from 'lucide-react'
|
import { Package, Settings, Building2 } from 'lucide-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
import { DatePicker } from '@/components/ui/date-picker'
|
import { DatePicker } from '@/components/ui/date-picker'
|
||||||
@ -24,7 +25,7 @@ import type {
|
|||||||
SellerConsumable,
|
SellerConsumable,
|
||||||
} from '../types/supply-creation.types'
|
} from '../types/supply-creation.types'
|
||||||
|
|
||||||
export function DetailedCatalogBlock({
|
export const DetailedCatalogBlock = React.memo(function DetailedCatalogBlock({
|
||||||
allSelectedProducts,
|
allSelectedProducts,
|
||||||
productRecipes,
|
productRecipes,
|
||||||
fulfillmentServices,
|
fulfillmentServices,
|
||||||
@ -129,7 +130,7 @@ export function DetailedCatalogBlock({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
// Компонент детальной карточки товара с рецептурой
|
// Компонент детальной карточки товара с рецептурой
|
||||||
interface ProductDetailCardProps {
|
interface ProductDetailCardProps {
|
||||||
|
@ -9,12 +9,17 @@
|
|||||||
|
|
||||||
import { Package, Plus } from 'lucide-react'
|
import { Package, Plus } from 'lucide-react'
|
||||||
import Image from 'next/image'
|
import Image from 'next/image'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { Badge } from '@/components/ui/badge'
|
import { Badge } from '@/components/ui/badge'
|
||||||
|
|
||||||
import type { ProductCardsBlockProps } from '../types/supply-creation.types'
|
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) {
|
if (!selectedSupplier) {
|
||||||
return (
|
return (
|
||||||
<div className="bg-white/10 backdrop-blur-xl border border-white/20 rounded-2xl p-6">
|
<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>
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
@ -8,13 +8,14 @@
|
|||||||
'use client'
|
'use client'
|
||||||
|
|
||||||
import { Search } from 'lucide-react'
|
import { Search } from 'lucide-react'
|
||||||
|
import React from 'react'
|
||||||
|
|
||||||
import { OrganizationAvatar } from '@/components/market/organization-avatar'
|
import { OrganizationAvatar } from '@/components/market/organization-avatar'
|
||||||
import { Input } from '@/components/ui/input'
|
import { Input } from '@/components/ui/input'
|
||||||
|
|
||||||
import type { SuppliersBlockProps } from '../types/supply-creation.types'
|
import type { SuppliersBlockProps } from '../types/supply-creation.types'
|
||||||
|
|
||||||
export function SuppliersBlock({
|
export const SuppliersBlock = React.memo(function SuppliersBlock({
|
||||||
suppliers,
|
suppliers,
|
||||||
selectedSupplier,
|
selectedSupplier,
|
||||||
searchQuery,
|
searchQuery,
|
||||||
@ -132,7 +133,7 @@ export function SuppliersBlock({
|
|||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
})
|
||||||
|
|
||||||
// Утилитарная функция для меток рынков (временно, потом перенести в хук)
|
// Утилитарная функция для меток рынков (временно, потом перенести в хук)
|
||||||
function getMarketLabel(market?: string) {
|
function getMarketLabel(market?: string) {
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
import { ArrowLeft } from 'lucide-react'
|
import { ArrowLeft } from 'lucide-react'
|
||||||
import { useRouter } from 'next/navigation'
|
import { useRouter } from 'next/navigation'
|
||||||
|
import React, { useCallback } from 'react'
|
||||||
|
|
||||||
import { Sidebar } from '@/components/dashboard/sidebar'
|
import { Sidebar } from '@/components/dashboard/sidebar'
|
||||||
import { Button } from '@/components/ui/button'
|
import { Button } from '@/components/ui/button'
|
||||||
@ -89,23 +90,30 @@ export function CreateSuppliersSupplyPage() {
|
|||||||
} = useRecipeBuilder({ selectedFulfillment })
|
} = useRecipeBuilder({ selectedFulfillment })
|
||||||
|
|
||||||
// Обработчики событий для блоков
|
// Обработчики событий для блоков
|
||||||
const handleSupplierSelect = (supplier: GoodsSupplier) => {
|
const handleSupplierSelect = useCallback(
|
||||||
|
(supplier: GoodsSupplier) => {
|
||||||
setSelectedSupplier(supplier)
|
setSelectedSupplier(supplier)
|
||||||
// Сбрасываем выбранные товары при смене поставщика
|
// Сбрасываем выбранные товары при смене поставщика
|
||||||
setAllSelectedProducts([])
|
setAllSelectedProducts([])
|
||||||
setSelectedGoods([])
|
setSelectedGoods([])
|
||||||
}
|
},
|
||||||
|
[setSelectedSupplier, setAllSelectedProducts, setSelectedGoods],
|
||||||
|
)
|
||||||
|
|
||||||
const handleProductAdd = (product: GoodsProduct) => {
|
const handleProductAdd = useCallback(
|
||||||
|
(product: GoodsProduct) => {
|
||||||
const quantity = getProductQuantity(product.id) || 1
|
const quantity = getProductQuantity(product.id) || 1
|
||||||
addProductToSelected(product, quantity)
|
addProductToSelected(product, quantity)
|
||||||
initializeProductRecipe(product.id)
|
initializeProductRecipe(product.id)
|
||||||
|
|
||||||
// Добавляем в корзину
|
// Добавляем в корзину
|
||||||
addToCart(product, quantity)
|
addToCart(product, quantity)
|
||||||
}
|
},
|
||||||
|
[getProductQuantity, addProductToSelected, initializeProductRecipe, addToCart],
|
||||||
|
)
|
||||||
|
|
||||||
const handleQuantityChange = (productId: string, quantity: number) => {
|
const handleQuantityChange = useCallback(
|
||||||
|
(productId: string, quantity: number) => {
|
||||||
updateSelectedProductQuantity(productId, quantity)
|
updateSelectedProductQuantity(productId, quantity)
|
||||||
|
|
||||||
// Синхронизируем с корзиной
|
// Синхронизируем с корзиной
|
||||||
@ -116,14 +124,19 @@ export function CreateSuppliersSupplyPage() {
|
|||||||
removeFromCart(productId)
|
removeFromCart(productId)
|
||||||
removeProductFromSelected(productId)
|
removeProductFromSelected(productId)
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
[updateSelectedProductQuantity, allSelectedProducts, addToCart, removeFromCart, removeProductFromSelected],
|
||||||
|
)
|
||||||
|
|
||||||
const handleRecipeChange = (productId: string, recipe: ProductRecipe) => {
|
const handleRecipeChange = useCallback(
|
||||||
|
(productId: string, recipe: ProductRecipe) => {
|
||||||
setProductRecipes((prev) => ({
|
setProductRecipes((prev) => ({
|
||||||
...prev,
|
...prev,
|
||||||
[productId]: recipe,
|
[productId]: recipe,
|
||||||
}))
|
}))
|
||||||
}
|
},
|
||||||
|
[setProductRecipes],
|
||||||
|
)
|
||||||
|
|
||||||
// Обработчик ошибок
|
// Обработчик ошибок
|
||||||
if (suppliersError) {
|
if (suppliersError) {
|
||||||
|
Reference in New Issue
Block a user