0

我在 NextJS 应用程序的主页上运行了一个函数,该函数接收从 getStaticProps 收到的道具。唯一的问题是,在 iOS 和 Mac 上,使用 Safari/DuckDuckGo 浏览器时,页面偶尔会(大约每 5/6 次无缓存重新加载以隐身模式)加载 -all- 内容......但没有一个是可见的。

您仍然可以复制和粘贴文本,您可以按下图像然后查看它们,但唯一可见的是标题背景颜色和 HTML/正文背景颜色。但是,它在所有操作系统上 100% 的时间都在 Chrome 中运行。

如果您认为问题出在此函数的长度和/或方法上,您能否为我提供有关如何压缩它或使用更好实践的指导?

如果您认为问题出在其他地方,我很想知道从哪里开始寻找。控制台没有显示任何问题。

这是正在渲染的组件:

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

这是我编写的用于组织渲染数据的函数:

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;
  }

当它起作用时: 图1

如果没有: 图 1 图 2

4

1 回答 1

0

您的问题似乎是由于了解渲染周期以及导致组件渲染的原因。任何这些更改都会导致组件再次呈现:

  1. 更新道具
  2. 更新状态
  3. 上下文值更改

每次上述更改之一(也可能由父组件触发)时,您const loadThis = organizeMenu(props);都会同步运行,这很浪费并导致 UI 组件不必要地呈现。

  const loadThis = organizeMenu(props);

  const bfast = loadThis.bfast;
  const entrees = loadThis.entrees;
  const drinks = loadThis.drinks;
  console.log(`bfast`, bfast);

将这些移动到state,useEffect理想情况下organizeMenu在构建时使用getStaticProps并将数据作为道具传递给组件来运行功能。

请参阅下面带有注释的简化示例。每次单击按钮时,organizeMenu(props)都会运行并将输出到控制台并再次导致列表呈现。

export default function IndexPage({ categories, menu }) {
  const [selected, setSelected] = useState(0);
  const [menuList, setMenuList] = useState();

  const organizeMenu = (data) => {
    console.info('running organizeMenu');

    return data.map((category) => {
      return {
        id: category,
        name: category.toString()
      };
    });
  };

  // bad, this will be run every time there is a state change i.e. when the button is clicked and setSelected
  const foo = organizeMenu(categories);

  const handleOnClick = () => {
    setSelected(selected + 1);
  };

  useEffect(() => {
    console.info('running useEffect');
    setMenuList(
      categories.map((category) => {
        return {
          id: category,
          name: category.toString()
        };
      })
    );
  }, [categories]);

  return (
    <div>
      Hello World. {selected}
      <br />
      {menu && menu.map((menuItem) => <li key={menuItem.id}>{menuItem.name}</li>)}
      <br />
      {menuList && menuList.map((menuItem) => <li key={menuItem.id}>{menuItem.name}</li>)}
      <hr />
      <button onClick={handleOnClick}>Make it so</button>
    </div>
  );
}

export async function getStaticProps(context) {
  const categories = [1, 2, 3];

  // prep menu here so run at build time
  const menu = categories.map((category) => {
    return {
      id: category,
      name: category.toString()
    };
  });

  return {
    props: {
      categories,
      menu
    }
  };
}

CodesandBox 演示

于 2021-09-24T20:18:25.687 回答