I have a function I run on my NextJS application's homepage that takes in the props received from getStaticProps. The only problem is that on iOS and Mac, using the Safari/DuckDuckGo browser, the page will occasionally (about every 5/6 cacheless reloads in incognito) load -all- content....but none of it is visible.
You can still copy and paste the text, you can push down on the images and THEN see them, but the only things visible are the header background color and HTML/Body background color. HOWEVER, it works 100% of the time in Chrome, on all operating systems.
If you think that the problem lies w/i the length and/or methodology of this function, can you please provide me with guidance on how to condense it, or use better practices?
And if you think the problem lies elsewhere, I'd love to know where to start looking. The console doesn't show any problems whatsoever.
Here's the component that is being rendered:
import React, { useState, useEffect } from "react";
import organizeMenu from "../models/orgMenu";
import {
Box,
Heading,
SimpleGrid,
Divider,
Center,
Container,
} from "@chakra-ui/react";
import ItemCard from "./molecules/ItemCard";
import { useMenuStore } from "../state/store";
import SearchBar from './search/SearchBar';
const HomeContainer = ({props}) => {
const { setStateModifierLists } = useMenuStore();
const modifierLists = props.data.objects.filter(
(object) => object.type === "MODIFIER_LIST"
);
useEffect(() => {
setStateModifierLists(modifierLists);
}, []);
const itemList = props.data.objects.filter(
(object) => object.type === "ITEM"
);
const categories = props.data.objects.filter(
(object) => object.type === "CATEGORY"
);
const loadThis = organizeMenu(props);
const bfast = loadThis.bfast;
const entrees = loadThis.entrees;
const drinks = loadThis.drinks;
console.log(`bfast`, bfast);
const [loaded, setLoaded] = useState(false);
const handleLoad = (e) => {
console.log("loaded");
setLoaded(true);
};
return (
<Box w="100%">
<Container>
<SearchBar categories={categories} itemList={itemList} />
</Container>
<Heading ml={3}>Breakfast</Heading>
<Divider />
<Center>
<SimpleGrid
m="0 auto"
alignItems="center"
spacing={6}
p="2"
columns={[1, null, 2, null, 3]}
>
{bfast.map((b) => (
<ItemCard modifierLists={modifierLists} key={b.id} item={b} />
))}
</SimpleGrid>
</Center>
<Heading ml={3}>Entrees</Heading>
<Divider />
<Center>
<SimpleGrid
m="0 auto"
alignItems="baseline"
onLoad={handleLoad}
spacing={6}
p="2"
columns={[1, null, 2, null, 3]}
>
{entrees.map((e) => (
<>
<ItemCard modifierLists={modifierLists} key={e.id} item={e} />
</>
))}
</SimpleGrid>
</Center>
<Heading ml={3}>Drinks</Heading>
<Divider />
<Center>
<SimpleGrid
m="0 auto"
alignItems="stretch"
onLoad={handleLoad}
spacing={6}
p="2"
columns={[1, null, 2, null, 3]}
>
{drinks.map((d) => (
<ItemCard modifierLists={modifierLists} key={d.id} item={d} />
))}
</SimpleGrid>
</Center>
</Box>
)
}
export default HomeContainer
This is the function I wrote to organize the data for rendering:
export default function organizeMenu(props) {
// Segment menu items
let menuItems = [];
menuItems = props.data.objects.filter((object) => object.type === "ITEM");
//Segment menu item images
let itemImages = [];
itemImages = props.data.objects.filter((object) => object.type === "IMAGE");
//Segment Categories
let categories = [];
categories = props.data.objects.filter(
(object) => object.type === "CATEGORY"
);
//Segment Modifier Lists
let modifierLists = [];
modifierLists = props.data.objects.filter(
(object) => object.type === "MODIFIER_LIST"
);
// Merge data to provide better mapping and ordering process
//Looping through menuItems and itemImages to combine fields into menuItems for ease of mapping data to components
for (let x = 0; x < menuItems.length; x++) {
menuItems[x].modifiers = [];
menuItems[x].imageData = {
url: "https://via.placeholder.com/250",
};
for (let y = 0; y < itemImages.length; y++) {
if (menuItems[x].imageId === itemImages[y].id) {
// console.log(`Match: ${menuItems[x].imageId}`);
menuItems[x].imageData = itemImages[y].imageData;
} else {
// console.log("No match");
}
}
}
// Next, we're going to tie the actual modifiers to the menuItem objects, rather than having to map them separately.
for (let mm = 0; mm < menuItems.length; mm++) {
menuItems[mm].availableModifiers = [];
if (menuItems[mm].itemData.modifierListInfo) {
for (
let xx = 0;
xx < menuItems[mm].itemData.modifierListInfo.length;
xx++
) {
if (menuItems[mm].itemData.modifierListInfo[xx].enabled === true) {
// console.log("enabled");
for (let zz = 0; zz < modifierLists.length; zz++) {
if (
menuItems[mm].itemData.modifierListInfo[xx].modifierListId ===
modifierLists[zz].id
) {
for (
let xo = 0;
xo < modifierLists[zz].modifierListData.modifiers.length;
xo++
) {
menuItems[mm].availableModifiers.push(
modifierLists[zz].modifierListData.modifiers[xo]
);
}
}
}
} else {
// console.log("no mods");
}
}
}
}
//If modifier has a price, map the price according to the needs of square's api.
for (let qu = 0; qu < menuItems.length; qu++) {
for (let xz = 0; xz < menuItems[qu].availableModifiers.length; xz++) {
if (menuItems[qu].availableModifiers[xz].modifierData.priceMoney) {
menuItems[qu].availableModifiers[xz].basePriceMoney = {
...menuItems[qu].availableModifiers[xz].modifierData.priceMoney,
};
} else {
menuItems[qu].availableModifiers[xz].modifierData.basePriceMoney = {
amount: "0",
currency: "USD",
};
menuItems[qu].availableModifiers[xz].basePriceMoney = {
amount: "0",
currency: "USD",
};
}
}
}
// Set primary variation (default)
for (let h = 0; h < menuItems.length; h++) {
if (
menuItems[h].itemData.variations[0] &&
menuItems[h].itemData.variations[0].isDeleted === false
) {
menuItems[h].primaryVariation = {
...menuItems[h].itemData.variations[0],
isChosen: false,
};
}
}
// Merging "CATEGORIES" with menuItems
for (let q = 0; q < menuItems.length; q++) {
menuItems[q].categoryName;
for (let w = 0; w < categories.length; w++) {
if (menuItems[q].itemData.categoryId === categories[w].id) {
menuItems[q].categoryName = categories[w].categoryData.name;
}
}
}
// Separating items into arrays based on category...
let breakfastItems = [];
breakfastItems = menuItems.filter(
(object) => object.categoryName === "Breakfast"
);
let entreeItems = [];
entreeItems = menuItems.filter((object) => object.categoryName === "Entree");
let drinkItems = [];
drinkItems = menuItems.filter((object) => object.categoryName === "Drinks");
// The object to be returned... Items are rendered from these objects.
const catalog = {
bfast: breakfastItems,
entrees: entreeItems,
drinks: drinkItems,
};
return catalog;
}
When it works: Image 1
Your issue seems due an understanding of the rendering cycle and what causes components to render. Any of these changes will cause the component to render again:
Your
const loadThis = organizeMenu(props);
is being run synchronously every time when one of the above changes (could be triggered by parent component too) and is wasteful and causing UI components to render unnecessarily.Move these into
state
,useEffect
, or ideally run theorganizeMenu
functionality at build time usinggetStaticProps
and pass the data to the component as props.See the simplified example with comments below. Every time you click the button,
organizeMenu(props)
is run and will output to the console and result in the list rendering again.CodesandBox Demo