next.js 修复了SizeGrid在react窗口的InfiniteLoader中不触发loadmoreitems的问题

rjjhvcjd  于 2023-10-18  发布在  React
关注(0)|答案(1)|浏览(130)

我是react js的新手,我想用react窗口创建一个产品列表。在使用Infiniteloader执行固定大小列表时,loadMoreItems工作正常,我能够进行API调用并将新的数据集追加到productList。但是我希望每行有3个产品的gridview,同时使用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;
roqulrg3

roqulrg31#

onItemsRendered={onItemsRendered}的默认示例存在错误
把这个 prop 改成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>
  );
}

相关问题