【问题标题】:Windowing in React/Gatsby/Chakra UIReact/Gatsby/Chakra UI 中的窗口化
【发布时间】:2021-11-25 14:34:46
【问题描述】:

我有一个使用 React、Gatsby 和 Chakra UI 框架的简单网络应用程序。该应用程序包含一个索引页面,该页面从 1000 多个 mdx 文件中查询 frontmatter,并呈现一个最小的摘要组件,其中包含来自 frontmatter 的 5 个字段和一个指向每个详细页面的链接。 1000 多个详细信息页面是在 gatsby-node.js 中使用 createPage 生成的。

索引页面使用 map 遍历每个 mdx 节点,并使用 Chakra UI 简单网格以及每个项目的其他一些 Chakra 组件。

应用部署到 Gatsby Cloud 时收到的灯塔报告将应用的性能评为 60/100,这主要是由于 DOM 中的元素过多(索引页面呈现的 1000 多个摘要元素)。

我已经查看了所有相关文档并在其他来源中搜索了 SO,但找不到可行的解决方案来仅渲染 html 以在任何给定点显示在屏幕上的 25 个左右项目并根据需要渲染其余部分而不是从一开始就渲染所有 1000+。

import * as React from "react";
import { ChakraProvider, chakra, Box, SimpleGrid, HStack, Button, VStack, Wrap, WrapItem, Badge } from "@chakra-ui/react";
import { graphql, useStaticQuery } from 'gatsby';

const IndexPage = () => {

    const query = useStaticQuery(graphql` 
    query AllObjects {
      allMdx(sort: {fields: frontmatter___field1}) {
        nodes {
          frontmatter {
            field1
            field2
            field3
            field4
            field5
            field6
            field7
            uniqueId
          }
        }
      }
    }
    `)

    return (
      <ChakraProvider>
        <body>
            <main>
                      <SimpleGrid columns={{base: 1, lg: 3, md: 2, sm:1}} spacing={{base: '1.5em', lg: '1.5em', md:'1.0', sm:'0.90em'}}>
                      { query.allMdx.nodes.map((node) => (
                          <Box key={node.frontmatter.field1} margin="2em" padding="1em">
                            <HStack padding="0.4em" align="center" alignItems="stretch" justifyContent="space-between">
                            <Button size="sm" shadow="md" colorScheme="blue"
                            onClick={(e) => {
                              e.preventDefault();
                              window.location.href=`/objects/${node.frontmatter.field1.toLowerCase()}`;
                              }}
                            >Detail</Button>
                            <Box align="center"/>
                            <VStack alignItems="end" justifyContent="right">
                            <Wrap columns={2} spacing={1} direction={["row-reverse"]} isInline="true" shouldWrapChildren="true">
                              <WrapItem>
                                {node.frontmatter.field5 === true &&
                                <Badge colorScheme="green">Field5</Badge>
                                }
                              </WrapItem>
                              <WrapItem>
                                {node.frontmatter.field6 === true &&
                                <Badge colorScheme="blue">Field6</Badge>
                                }
                              </WrapItem>
                              <WrapItem>
                                {node.frontmatter.field7 === true &&
                                <Badge colorScheme="orange">field7</Badge>
                                }
                              </WrapItem>
                              <WrapItem>
                                {node.frontmatter.field4 === true &&
                                <Badge colorScheme="red">Field8</Badge>
                                }
                              </WrapItem>
                            </Wrap>
                            </VStack>
                            </HStack>
                            <Box bg="gray.300" borderRadius="0.5em" margin="0em" padding="0em">
                            <chakra.h2 id={node.frontmatter.field1.toLowerCase()}>
                              Field1: {node.frontmatter.field1}
                            </chakra.h2>
                            <chakra.p>Field2: {node.frontmatter.field2}</chakra.p>
                            <chakra.p>{node.frontmatter.field3}</chakra.p>
                            </Box>
                          </Box>
                      ))}
                      </SimpleGrid>

            </main>
        </body>
      </ChakraProvider>
    )
  };

export default IndexPage;

【问题讨论】:

    标签: reactjs gatsby chakra-ui


    【解决方案1】:

    好吧,您找到了解决方案。使用无限滚动或一些类似的延迟(按钮等)方法来按需渲染全部网格项,而不是同时渲染所有项。

    只需创建一个包含切片数量的元素的状态 (useState),并在用户滚动页面后立即升级它们。这将节省(并延迟)您的初始 DOM 元素。

    我将添加一个基于按钮的方法来渲染更多元素,但使用无限滚动的想法完全相同。

    const IndexPage = () => {
        const query = useStaticQuery(graphql` 
        query AllObjects {
          allMdx(sort: {fields: frontmatter___field1}) {
            nodes {
              frontmatter {
                field1
                field2
                field3
                field4
                field5
                field6
                field7
                uniqueId
              }
            }
          }
        }
        `)
      // Array of all news articles
      const allGridElements = query.allMdx.nodes
    
      // State for the list
      const [list, setList] = useState([...allGridElements.slice(0, 10)])
    
      // State to trigger the load more
      const [loadMore, setLoadMore] = useState(false)
    
      // State of whether there is more to load
      const [hasMore, setHasMore] = useState(allGridElements.length > 10)
    
      // Load more button click
      const handleLoadMore = () => {
        setLoadMore(true)
      }
    
      // Handle loading more articles
      useEffect(() => {
        if (loadMore && hasMore) {
          const currentLength = list.length
          const isMore = currentLength < allGridElements.length
          const nextResults = isMore
            ? allGridElements.slice(currentLength, currentLength + 10)
            : []
          setList([...list, ...nextResults])
          setLoadMore(false)
        }
      }, [loadMore, hasMore]) //eslint-disable-line
    
      //Check if there is more
      useEffect(() => {
        const isMore = list.length < allGridElements.length
        setHasMore(isMore)
      }, [list]) //eslint-disable-line
    
      return (
        <div>
          <h1>Load more demo</h1>
          <div>
            {list.map((item) => (
            { /* Your JSX rendering the grid items */ }
            ))}
          </div>
          {hasMore ? (
            <button onClick={handleLoadMore}>Load More</button>
          ) : (
            <p>No more results</p>
          )}
        </div>
      )
    }
    
    export default IndexPage
    

    注意:为了避免无休止的回答,我省略了循环返回的 JSX。只需将其放在评论中即可。

    这是不言自明的,您将所有元素设置为 React 状态 (useState) 并遍历它。 useEffect负责基于监听器升级列表。

    其他有用的资源:

    【讨论】:

    • 太好了,谢谢!我试试看。
    猜你喜欢
    • 2021-04-15
    • 2020-12-30
    • 2022-11-11
    • 2022-07-18
    • 1970-01-01
    • 2022-11-10
    • 2021-04-10
    • 1970-01-01
    • 2021-06-17
    相关资源
    最近更新 更多