mirror of
https://github.com/duckietm/Nitro-V3.git
synced 2026-06-19 06:56:20 +00:00
fix(catalog): stabilise hook order in CatalogPurchaseWidgetView
React reported "Rendered more hooks than during the previous render" when CatalogPurchaseWidgetView transitioned from currentOffer=null to a real offer: hook count jumped from 22 to 23 because the useMemo/useEffect block for the builders-club placement state sat *below* the `if(!currentOffer) return null` early-return on line 140. On the first render it never ran; on the next render (offer loaded) it did, and React's hook-call tracker flagged the divergence and unmounted the component via the error boundary. Fix: move the three builders-club hooks (useMemo builderPlaceableStatus, useMemo buildersClubPlaceOneButtonStyle, useEffect interval) above the early return. They already short-circuit cleanly when isBuildersClubPlaceable is false — added a defensive `!currentOffer` guard on the first useMemo and an explicit `!!currentOffer` clause on the derived isBuildersClubPlaceable so the .product access stays safe when offer is null. Behavior unchanged for the loaded-offer path; the early-render path now runs the hooks but their bodies no-op. Verification: yarn typecheck clean, yarn test 209/209.
This commit is contained in:
@@ -137,15 +137,19 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
|
||||
};
|
||||
}, [ purchaseState ]);
|
||||
|
||||
if(!currentOffer) return null;
|
||||
|
||||
// Builders-club state — derived + hooks MUST run unconditionally on
|
||||
// every render so the hook order stays stable even when currentOffer
|
||||
// is null (the `if(!currentOffer) return null` below would otherwise
|
||||
// hide the useMemo/useEffect block from the first render and React
|
||||
// would flag "Rendered more hooks than during the previous render").
|
||||
const isBuildersClubOffer = (currentType === CatalogType.BUILDER);
|
||||
const isBuildersClubPlaceable = isBuildersClubOffer
|
||||
&& !!currentOffer
|
||||
&& !!currentOffer.product
|
||||
&& ((currentOffer.product.productType === ProductTypeEnum.FLOOR) || (currentOffer.product.productType === ProductTypeEnum.WALL));
|
||||
const builderPlaceableStatus = useMemo(() =>
|
||||
{
|
||||
if(!isBuildersClubPlaceable || !getBuilderFurniPlaceableStatus) return BuilderFurniPlaceableStatus.OKAY;
|
||||
if(!isBuildersClubPlaceable || !getBuilderFurniPlaceableStatus || !currentOffer) return BuilderFurniPlaceableStatus.OKAY;
|
||||
|
||||
return getBuilderFurniPlaceableStatus(currentOffer);
|
||||
}, [ currentOffer, getBuilderFurniPlaceableStatus, isBuildersClubPlaceable, builderPlaceableRefreshTick ]);
|
||||
@@ -164,6 +168,8 @@ export const CatalogPurchaseWidgetView: FC<CatalogPurchaseWidgetViewProps> = pro
|
||||
return () => clearInterval(interval);
|
||||
}, [ isBuildersClubPlaceable ]);
|
||||
|
||||
if(!currentOffer) return null;
|
||||
|
||||
const PurchaseButton = () =>
|
||||
{
|
||||
if(isBuildersClubPlaceable)
|
||||
|
||||
Reference in New Issue
Block a user