diff --git a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx index d5321e0..ed1dc34 100644 --- a/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx +++ b/src/components/catalog/views/page/widgets/CatalogPurchaseWidgetView.tsx @@ -137,15 +137,19 @@ export const CatalogPurchaseWidgetView: FC = 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 = pro return () => clearInterval(interval); }, [ isBuildersClubPlaceable ]); + if(!currentOffer) return null; + const PurchaseButton = () => { if(isBuildersClubPlaceable)