react-virtualized Grid 行高不一致,添加新项目时滚动位置发生变化

问题描述 投票:0回答:1

下面是我的代码,当我在移动设备上每行渲染 2 个项目,在桌面设备上每行渲染 4 个项目时,我尝试实现产品列表页面。

当我给 rowHeight 一个固定的高度时,它可以在最新的设备上正确渲染,但是当设备分辨率较低时,它会在底部添加额外的空间。 height Issue in bottom

解决此问题,当我使用 CellMeasurer 和 CellMeasurerCache 时,高度不一致,并且当向项目添加新项目时滚动位置也会发生变化。请让我知道我在这里做错了。

import React, { useEffect } from 'react';
import {
  Grid,
  WindowScroller,
  AutoSizer,
  CellMeasurerCache,
  CellMeasurer,
} from 'react-virtualized';
import _get from 'lodash/get';
import { Grid as MuiGrid } from '@material-ui/core';
import { makeStyles } from '@material-ui/core/styles';
import PlpListItem from './PlpListItem';
import { imageTypeDecider } from '../../utils/helper';
import 'react-virtualized/styles.css';


export const isPhone = (deviceType) => deviceType === 'phone' ? true : false;

export const calculateDynamicWidth = (deviceType, rowWidth, itemCount) => {
  let maxItemsPerRow;
  if (itemCount !== 0 && rowWidth !== 0) {
    if (isPhone(deviceType)) {
      maxItemsPerRow = Math.min(2, itemCount);
    } else {
      maxItemsPerRow = Math.min(4, itemCount);
    }
  } else {
    maxItemsPerRow = 1
  }
  return rowWidth / maxItemsPerRow;
};

export const calculateColumnCount = (rowWidth, dynamicItemWidth) => {
  return Math.floor(rowWidth / dynamicItemWidth);
};

export const plpProductHeight = (deviceType, imageKey) => {
  let height;
  if (isPhone(deviceType)) {
    height = imageKey === "345WX345H" ? 315 : 415;
  } else {
    height = imageKey === "345WX345H" ? 340 : 450;
  }
  return height;
};

export const serverSideHeightAndWidth = (deviceType) => {
  if (isPhone(deviceType)) {
    return {
      serverSideHeight: 1000,
      serverSideWidth: 430
    };
  } else {
    return {
      serverSideHeight: 2000,
      serverSideWidth: 1028
    };
  }
};

const useStyles = makeStyles((theme) => ({
  grid: {
    justifyContent: 'center',
    overflow: 'visible !important'
  },
  row: {
    display: 'flex',
    justifyContent: 'flex-start',
    padding: '16px',
    maxWidth: '260px',
    marginTop: '-8px',
    [theme.breakpoints.down("sm")]: {
      padding: '0',
      maxWidth: '215px',
    }
  }
}));

const PlpList = React.memo((props) => {
  const classes = useStyles();
  const {
    device: { type: deviceType = '' } = {},
    device = {},
    dispatch,
    concept = '',
    lang = 'en',
    isSpider,
    country = 'in',
    prefixUrl = '',
    isServer = '',
    configResponse = {},
    filterReducer = {},
    userResponse = {},
    favList
  } = props || {};
  const configImageFormat = _get(props, 'configResponse.data.mediaConfig.plpMediaConfig.imageConfig.imageType', 'square');
  const enableRectImagesFlag = _get(props, 'configResponse.data.mediaConfig.plpMediaConfig.enabled', false);
  const algoliaImageFormat = _get(props, 'algoliaCatResponse.data.imageFormat', '');
  const queryID = _get(props, 'algoliaProdResponse.data.queryID');
  const page = _get(props, 'routerReducer.router.query.page');
  let items = _get(props, 'algoliaProdResponse.data.hits', []);
  const imageDetails = imageTypeDecider(concept, configImageFormat, algoliaImageFormat, enableRectImagesFlag);
  const { imageFormat = '', imageLoader = '', imageKey = '', imageKey2 = '' } = imageDetails || {};
  const itemHeight = plpProductHeight(deviceType, imageKey);
  const { serverSideHeight = 0, serverSideWidth = 0 } = serverSideHeightAndWidth(deviceType);

  const cache = new CellMeasurerCache({
    keyMapper: (rowIndex) => items[rowIndex].objectID,
    fixedWidth: true,
    defaultHeight: itemHeight,
  });

  return (
    <MuiGrid container>
      <AutoSizer disableHeight defaultHeight={serverSideHeight} defaultWidth={serverSideWidth}>
        {({ width: rowWidth }) => {
          const itemCount = items.length;
          const dynamicItemWidth = calculateDynamicWidth(deviceType, rowWidth, itemCount);
          return (
            <WindowScroller serverHeight={serverSideHeight} serverWidth={serverSideWidth}>
              {({ height, scrollTop }) => (
                <Grid
                  className={classes.grid}
                  autoHeight
                  height={height}
                  scrollTop={scrollTop}
                  width={rowWidth}
                  rowCount={Math.ceil(itemCount / calculateColumnCount(rowWidth, dynamicItemWidth))}
                  columnCount={calculateColumnCount(rowWidth, dynamicItemWidth)}
                  rowHeight={cache.rowHeight}
                  columnWidth={dynamicItemWidth}
                  overscanRowCount={10}
                  overscanColumnCount={10}
                  cellRenderer={({ rowIndex, columnIndex, key, style, parent }) => {
                    const itemIndex = rowIndex * calculateColumnCount(rowWidth, dynamicItemWidth) + columnIndex;
                    if (itemIndex < itemCount) {
                      const item = items[itemIndex];
                      return (
                        <CellMeasurer
                          cache={cache}
                          columnIndex={columnIndex}
                          key={key}
                          parent={parent}
                          rowIndex={rowIndex}
                        >
                          {({ registerChild, measure }) => (
                            <div style={style} key={key} className={classes.row} ref={registerChild}>
                              <PlpListItem
                                measure={measure}
                                page={page}
                                key={itemIndex}
                                dispatch={dispatch}
                                configResponse={configResponse}
                                filterReducer={filterReducer}
                                userResponse={userResponse}
                                favList={favList}
                                isSpider={isSpider}
                                index={itemIndex}
                                queryID={queryID}
                                product={item}
                                classes={undefined}
                                imageLoader={imageLoader}
                                imageFormat={imageFormat}
                                imageKey={imageKey}
                                imageKey2={imageKey2}
                                lang={lang}
                                concept={concept}
                                country={country}
                                device={device}
                                prefixUrl={prefixUrl}
                                isServer={isServer}
                                type="PLP"
                                subCategory={_get(props, 'router.query.cid')}
                              />
                            </div>
                          )}
                        </CellMeasurer>
                      );
                    }
                  }}
                />
              )}
            </WindowScroller>
          );
        }}
      </AutoSizer>
    </MuiGrid>
  );
});

export default PlpList;

我希望高度是动态的,并且在添加新项目时不应移动滚动位置

reactjs next.js react-virtualized row-height
1个回答
0
投票

确保调用测量:确认在 PlpListItem 中调用测量以准确测量其高度。 处理动态内容:如果 PlpListItem 呈现动态内容(如图像),请使用 useEffect 在内容更改时触发重新测量。 更新时清除缓存:添加或删除项目时清除cache.clearAll()以更新缓存的高度。

保留滚动位置:使用状态变量(例如,scrollPosition)来存储更新前的滚动位置。 更新后恢复:添加项目后,将Grid的scrollTop设置为保留的scrollPosition。

代码改进

other imports
import { useState, useEffect } from 'react';

const PlpList = React.memo((props) => {
  // 

  const [scrollPosition, setScrollPosition] = useState(0);

  useEffect(() => {
    // Clear cache when items change
    if (prevItems !== items) {
      cache.clearAll();
      setScrollPosition(0); // Reset scroll position on item changes
    }
  }, [items, cache, prevItems]);

  // 

  return (
    // ... other JSX
      <Grid
        // ... other props
        scrollTop={scrollTop} // Set scroll position
      >
        // ... CellMeasurer and CellRenderer
      </Grid>
    </WindowScroller>
  );
});

© www.soinside.com 2019 - 2024. All rights reserved.