diff --git a/src/app/WardrobePage/Item.js b/src/app/WardrobePage/Item.js index 4289cfe..64b2b67 100644 --- a/src/app/WardrobePage/Item.js +++ b/src/app/WardrobePage/Item.js @@ -86,7 +86,7 @@ function Item({ icon={} label="Remove" onClick={(e) => { - onRemove(); + onRemove(item.id); e.preventDefault(); }} /> diff --git a/src/app/WardrobePage/ItemsPanel.js b/src/app/WardrobePage/ItemsPanel.js index 3a1c265..488e528 100644 --- a/src/app/WardrobePage/ItemsPanel.js +++ b/src/app/WardrobePage/ItemsPanel.js @@ -117,6 +117,13 @@ function ItemZoneGroup({ } }; + const onRemove = React.useCallback( + (itemId) => { + dispatchToOutfit({ type: "removeItem", itemId }); + }, + [dispatchToOutfit] + ); + return ( @@ -136,9 +143,7 @@ function ItemZoneGroup({ !isDisabled && outfitState.wornItemIds.includes(item.id) } isInOutfit={outfitState.allItemIds.includes(item.id)} - onRemove={() => - dispatchToOutfit({ type: "removeItem", itemId: item.id }) - } + onRemove={onRemove} isDisabled={isDisabled} /> ); diff --git a/src/app/WardrobePage/useOutfitState.js b/src/app/WardrobePage/useOutfitState.js index ab56616..3a6b196 100644 --- a/src/app/WardrobePage/useOutfitState.js +++ b/src/app/WardrobePage/useOutfitState.js @@ -72,7 +72,39 @@ function useOutfitState() { } ); - const items = data?.items || []; + const resultItems = data?.items || []; + + // Okay, time for some big perf hacks! Lower down in the app, we use + // React.memo to avoid re-rendering Item components if the items haven't + // updated. In simpler cases, we just make the component take the individual + // item fields as props... but items are complex and that makes it annoying + // :p Instead, we do these tricks to reuse physical item objects if they're + // still deep-equal to the previous version. This is because React.memo uses + // object identity to compare its props, so now when it checks whether + // `oldItem === newItem`, the answer will be `true`, unless the item really + // _did_ change! + const [cachedItemObjects, setCachedItemObjects] = React.useState([]); + let items = resultItems.map((item) => { + const cachedItemObject = cachedItemObjects.find((i) => i.id === item.id); + if ( + cachedItemObject && + JSON.stringify(cachedItemObject) === JSON.stringify(item) + ) { + return cachedItemObject; + } + return item; + }); + if ( + items.length === cachedItemObjects.length && + items.every((_, index) => items[index] === cachedItemObjects[index]) + ) { + // Even reuse the entire array if none of the items changed! + items = cachedItemObjects; + } + React.useEffect(() => { + setCachedItemObjects(items); + }, [items, setCachedItemObjects]); + const itemsById = {}; for (const item of items) { itemsById[item.id] = item;