下面是我的代码,当我在移动设备上每行渲染 2 个项目,在桌面设备上每行渲染 4 个项目时,我尝试实现产品列表页面。
当我给 rowHeight 一个固定的高度时,它可以在最新的设备上正确渲染,但是当设备分辨率较低时,它会在底部添加额外的空间。
解决此问题,当我使用 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;
我希望高度是动态的,并且在添加新项目时不应移动滚动位置
确保调用测量:确认在 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>
);
});