mirror of
https://github.com/christianvidalwolf-prog/CrazeAnalytix.git
synced 2026-08-03 11:45:23 +02:00
perf: make filter dropdowns feel instant with useDeferredValue
Use React's useDeferredValue to decouple UI responsiveness from expensive data reprocessing. Checkboxes now update immediately on click; filterData/filterAdsData/filterBsrData and all downstream useMemos run as a low-priority background pass. Added local optimistic state in MultiSelectDropdown as a belt-and-suspenders layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Sonnet 4.6
parent
8e907f1dff
commit
dcf77cad2d
@@ -1,5 +1,5 @@
|
||||
|
||||
import React, { useState, useMemo, useEffect, useCallback, Suspense, lazy } from 'react';
|
||||
import React, { useState, useMemo, useEffect, useCallback, Suspense, lazy, useDeferredValue } from 'react';
|
||||
import FileUpload from './components/FileUpload';
|
||||
import Dashboard from './components/Dashboard';
|
||||
import FilterBar from './components/FilterBar';
|
||||
@@ -82,6 +82,7 @@ const App: React.FC = () => {
|
||||
woc: [],
|
||||
bulkSearch: '',
|
||||
});
|
||||
const deferredFilters = useDeferredValue(filters);
|
||||
|
||||
const top50Mode = useMemo(() => {
|
||||
const hasUK = filters.customer.includes('Amazon UK');
|
||||
@@ -525,8 +526,8 @@ const App: React.FC = () => {
|
||||
return metaMap;
|
||||
}, [rawData]);
|
||||
|
||||
const filteredData = useMemo(() => filterData(rawData, filters, stockMap, vendorStockMap, top50Mode), [rawData, filters, stockMap, vendorStockMap, top50Mode]);
|
||||
const filteredAdsData = useMemo(() => filterAdsData(adsData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode), [adsData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
|
||||
const filteredData = useMemo(() => filterData(rawData, deferredFilters, stockMap, vendorStockMap, top50Mode), [rawData, deferredFilters, stockMap, vendorStockMap, top50Mode]);
|
||||
const filteredAdsData = useMemo(() => filterAdsData(adsData, deferredFilters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode), [adsData, deferredFilters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
|
||||
const aggregatedData = useMemo(() => aggregateData(filteredData), [filteredData]);
|
||||
|
||||
// Calculate country-aware Top 50 Best Sellers for 2025
|
||||
@@ -565,8 +566,8 @@ const App: React.FC = () => {
|
||||
|
||||
// Derived Data for Vendor (BSR)
|
||||
const filteredBsrData = useMemo(() => {
|
||||
return filterBsrData(bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode);
|
||||
}, [bsrData, filters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
|
||||
return filterBsrData(bsrData, deferredFilters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode);
|
||||
}, [bsrData, deferredFilters, globalAsinMetadata, stockMap, vendorStockMap, top50Mode]);
|
||||
|
||||
// Combine Sales & Ads Data dynamically based on current filters
|
||||
const combinedAdsData = useMemo(() => {
|
||||
@@ -595,7 +596,7 @@ const App: React.FC = () => {
|
||||
// Only apply the "Max Week" cutoff if the user has NOT explicitly selected a specific time period.
|
||||
// If Month or Week filters are active, we show exactly what was asked (e.g., Full Year 2024).
|
||||
// If no time filters are active, we default to YTD (Like-for-Like) comparison.
|
||||
const isTimeFilterActive = filters.month.length > 0 || filters.week.length > 0;
|
||||
const isTimeFilterActive = deferredFilters.month.length > 0 || deferredFilters.week.length > 0;
|
||||
|
||||
if (!combinedAdsData) return [];
|
||||
|
||||
@@ -607,25 +608,25 @@ const App: React.FC = () => {
|
||||
if (!item.week) return true;
|
||||
return item.week <= gridContext.maxWeek;
|
||||
});
|
||||
}, [combinedAdsData, gridContext.maxWeek, filters.month, filters.week]);
|
||||
}, [combinedAdsData, gridContext.maxWeek, deferredFilters.month, deferredFilters.week]);
|
||||
|
||||
// Derived Data for Dashboard (YTD Filtered - Isolated)
|
||||
// Dashboard needs aggregated data respecting the max week limit of current year.
|
||||
// Dashboard needs aggregated data respecting the max week limit of current year...
|
||||
// UNLESS the user explicitly filters for a period.
|
||||
const ytdFilteredData = useMemo(() => {
|
||||
const isTimeFilterActive = filters.month.length > 0 || filters.week.length > 0;
|
||||
const isTimeFilterActive = deferredFilters.month.length > 0 || deferredFilters.week.length > 0;
|
||||
if (isTimeFilterActive) return filteredData;
|
||||
|
||||
return filteredData.filter(r => r.week <= gridContext.maxWeek);
|
||||
}, [filteredData, gridContext.maxWeek, filters.month, filters.week]);
|
||||
}, [filteredData, gridContext.maxWeek, deferredFilters.month, deferredFilters.week]);
|
||||
|
||||
const ytdFilteredAdsData = useMemo(() => {
|
||||
const isTimeFilterActive = filters.month.length > 0 || filters.week.length > 0;
|
||||
const isTimeFilterActive = deferredFilters.month.length > 0 || deferredFilters.week.length > 0;
|
||||
if (isTimeFilterActive) return filteredAdsData;
|
||||
|
||||
return filteredAdsData.filter(r => r.week <= gridContext.maxWeek);
|
||||
}, [filteredAdsData, gridContext.maxWeek, filters.month, filters.week]);
|
||||
}, [filteredAdsData, gridContext.maxWeek, deferredFilters.month, deferredFilters.week]);
|
||||
|
||||
const ytdAggregatedData = useMemo(() => {
|
||||
return aggregateData(ytdFilteredData);
|
||||
@@ -637,7 +638,7 @@ const App: React.FC = () => {
|
||||
// Derive Context Data (Product Line Context when drilling down)
|
||||
const contextAggregatedData = useMemo(() => {
|
||||
// Check if we are filtering by specific items (SKU, ASIN, Title)
|
||||
const hasItemFilters = filters.sku.length > 0 || filters.asin.length > 0 || filters.title.length > 0;
|
||||
const hasItemFilters = deferredFilters.sku.length > 0 || deferredFilters.asin.length > 0 || deferredFilters.title.length > 0;
|
||||
|
||||
if (!hasItemFilters || filteredData.length === 0) {
|
||||
return null;
|
||||
@@ -648,7 +649,7 @@ const App: React.FC = () => {
|
||||
|
||||
// 2. Create a "broad" filter: Keep Year/Customer/Month, but CLEAR Item filters, and restrict to these Lines
|
||||
const contextFilters: FilterState = {
|
||||
...filters,
|
||||
...deferredFilters,
|
||||
line: activeLines, // Force these lines
|
||||
sku: [], // Clear specific item filters
|
||||
asin: [],
|
||||
@@ -660,7 +661,7 @@ const App: React.FC = () => {
|
||||
const broadData = filterData(rawData, contextFilters);
|
||||
return aggregateData(broadData);
|
||||
|
||||
}, [rawData, filters, filteredData]);
|
||||
}, [rawData, deferredFilters, filteredData]);
|
||||
|
||||
|
||||
// Derive Options for Filter Dropdowns
|
||||
|
||||
Reference in New Issue
Block a user