FixedSizeGrid 未触发 react-window 的 InfiniteLoader 内的 loadmoreitems

FixedSizeGrid not triggering loadmoreitems inside the InfiniteLoader of react-window

提问人:Vishwa 提问时间:9/5/2023 最后编辑:James ZVishwa 更新时间:10/8/2023 访问量:69

问:

我是 react js 的新手,我希望使用 react-window 创建一个产品列表。在使用 Infiniteloader 执行 fixedsize 列表时,loadMoreItems 工作正常,我能够进行 API 调用并将新数据集附加到 productList。但是我希望每行使用 3 个产品进行网格视图,同时使用 InfinitLoader 中的 FixedSizeGrid 执行此操作,我无法在滚动列表时进行 API 调用。有没有解决方案或替代方法?

import React, { useEffect, useState } from "react";
import Image from 'next/image'
import { FixedSizeList as List, FixedSizeGrid as Grid  } from 'react-window'
import InfiniteLoader from 'react-window-infinite-loader'
import Paper from '@mui/material/Paper';
import { styled } from '@mui/material/styles';
import axios from 'axios';
import { Box, Stack, Typography } from '@mui/material';

// Custom imports
import { poppinsBold } from '../../shared/appfonts';

// Variables used for infinite virtualized list
const PAGE_SIZE = 20;
const TOTAL_PRODUCTS = 1000;
const LOAD_THRESHOLD = 200;
const itemSize = 200; // Fixed item size for 3 items per row
const columnCount = 3; // Number of columns (items per row)

const ProductList = () => {
    const [products, setProducts] = useState([]); // State to maintain product list data
    const [isLoading, setIsLoading] = useState(false); // State to maintain loading state while fetching products from the server
    const [currentPage, setCurrentPage] = useState(1); // State to maintain the current page record

    // Custom Title MUI component for UI
    const TitleTypography = styled(Typography)({
        fontFamily: poppinsBold.style.fontFamily,
        color: 'black',
    });

    // Custom Medium MUI component for UI
    const MediumTypography = styled(Typography)({
        fontFamily: poppinsBold.style.fontFamily,
        color: 'black',
    });

    // Function used to fetch products from the server
    const fetchProducts = async (pageNumber = currentPage) => {
        setIsLoading(true);
        try {
            const response = await axios.get(
                `http://localhost:5000/products?_page=${pageNumber}&_limit=${PAGE_SIZE}`
            );
            const newProducts = response.data;
            setProducts([...products, ...newProducts]);
            setCurrentPage(currentPage + 1);
        } catch (error) {
            console.error('Error fetching products:', error);
        } finally {
            setIsLoading(false);
        }
    };

    // Function used to load more product items to the infinite list
    const loadMoreItems = () => {
        console.log('load more items called')
        if (!isLoading) {
            console.log("currentPage", currentPage);
            if (products.length < TOTAL_PRODUCTS) {
                fetchProducts(currentPage);
            }
        }
    };

    useEffect(() => {
        fetchProducts();
    }, []); // Fetch initial data

    const renderItem = ({ columnIndex, rowIndex, style }) => {
        const index = rowIndex * columnCount + columnIndex;
        const product = products[index];

        return (
            <div style={style} key={product?.id}>
                <Paper sx={{ m: 3 }} elevation={3}>
                    <Stack display={'flex'} direction={'row'}>
                        <Image
                            width={120}
                            height={150}
                            src={
                                'https://images.pexels.com/photos/10863290/pexels-photo-10863290.jpeg?auto=compress&cs=tinysrgb&w=600'
                            }
                            alt="Alternate image"
                            style={{ objectFit: 'fill' }}
                        />
                        <Box
                            display={'flex'}
                            alignItems={'center'}
                            flexDirection={'column'}
                            justifyContent={'center'}
                            width={'100%'}
                        >
                            <TitleTypography>{product?.name}</TitleTypography>
                            <MediumTypography>${product?.price}</MediumTypography>
                        </Box>
                    </Stack>
                </Paper>
            </div>
        );
    };

    return (
        <Box>
            <InfiniteLoader
                isItemLoaded={() => isLoading}
                itemCount={TOTAL_PRODUCTS}
                loadMoreItems={loadMoreItems}
            >
                {({ onItemsRendered, ref }) => (
                    <Grid
                        height={window.innerHeight}
                        width={window.innerWidth}
                        columnCount={columnCount}
                        columnWidth={window.innerWidth / columnCount}
                        rowCount={Math.ceil(products.length / columnCount)}
                        rowHeight={200}
                        onItemsRendered={onItemsRendered}
                        ref={ref}
                    >
                        {renderItem}
                    </Grid>
                )}
            </InfiniteLoader>
        </Box>
    )
}

export default ProductList;
reactjs next.js material-ui react-window

评论


答:

0赞 Nikhil Shrestha 10/8/2023 #1

onItemsRendered={onItemsRendered} 的默认示例存在错误

更改此道具为我解决了问题。onItemsRendered={gridProps => { onItemsRendered({ overscanStartIndex: gridProps.overscanRowStartIndex * NUM_COLUMNS, overscanStopIndex: gridProps.overscanRowStopIndex * NUM_COLUMNS, visibleStartIndex: gridProps.visibleRowStartIndex * NUM_COLUMNS, visibleStopIndex: gridProps.visibleRowStopIndex * NUM_COLUMNS }); }}

import { FixedSizeGrid as Grid } from "react-window";
import InfiniteLoader from "react-window-infinite-loader";

const LOADING = 1;
const LOADED = 2;
const NUM_COLUMNS = 3;
let itemStatusMap = {};

const isItemLoaded = index => !!itemStatusMap[index];
const loadMoreItems = (startIndex, stopIndex) => {
  for (let index = startIndex; index <= stopIndex; index++) {
    itemStatusMap[index] = LOADING;
  }
  return new Promise(resolve =>
    setTimeout(() => {
      for (let index = startIndex; index <= stopIndex; index++) {
        itemStatusMap[index] = LOADED;
      }
      resolve();
    }, 2500)
  );
};

class Cell extends PureComponent {
  render() {
    const { columnIndex, rowIndex, style } = this.props;
    let label;
    const itemIndex = rowIndex * NUM_COLUMNS + columnIndex;
    if (itemStatusMap[itemIndex] === LOADED) {
      label = `Cell (${rowIndex}, ${columnIndex})`;
    } else {
      label = "Loading...";
    }
    return (
      <div className="ListItem" style={style}>
        {label}
      </div>
    );
  }
}

export default function App() {
  return (
    <Fragment>
      <p className="Note">
        This demo app mimics loading remote data with a 2.5s timer. While rows
        are "loading" they will display a "Loading..." label. Once data has been
        "loaded" the row number will be displayed. Start scrolling the list to
        automatically load data.
      </p>
      <InfiniteLoader
        isItemLoaded={isItemLoaded}
        itemCount={1000}
        loadMoreItems={loadMoreItems}
      >
        {({ onItemsRendered, ref }) => (
          <Grid
            className="List"
            columnCount={NUM_COLUMNS}
            columnWidth={100}
            height={150}
            rowCount={1000}
            rowHeight={35}
            onItemsRendered={gridProps => {
              onItemsRendered({
                overscanStartIndex:
                  gridProps.overscanRowStartIndex * NUM_COLUMNS,
                overscanStopIndex: gridProps.overscanRowStopIndex * NUM_COLUMNS,
                visibleStartIndex: gridProps.visibleRowStartIndex * NUM_COLUMNS,
                visibleStopIndex: gridProps.visibleRowStopIndex * NUM_COLUMNS
              });
            }}
            ref={ref}
            width={300}
          >
            {Cell}
          </Grid>
        )}
      </InfiniteLoader>
      <p className="Note">
        Check out the documentation to learn more:
        <br />
        <a href="https://github.com/bvaughn/react-window-infinite-loader#documentation">
          github.com/bvaughn/react-window-infinite-loader
        </a>
      </p>
    </Fragment>
  );
}