我有一个React组件,可以显示图像网格。网格:
- 必须提供分页。(显示150个图像的批次,允许转到下一个和上一个150个图像)。
- 必须允许选择图像(单击进行单选,单击时按住ctrl键进行多个单选,单击时按住shift键进行多个相邻选择。
- 选择必须沿着所有文件,而不仅仅是显示的文件。
我所面临的问题是图像选择非常不充分。如果单击图像,有时可能需要3-4秒才能选中。因此,要么handleClickOnGridItem
函数是超级无效的,要么发生了一些奇怪的事情。
**如果问题的原因确实是功能效率低下,如果您能提供一个满足要求且不会导致我遇到的问题的解决方案,我将非常感激。
如果你去the CodeSandbox MRE,你会明白我在说什么。尝试上传500张或更多图片,您将看到。
下面是我的组件:
import {
useState,
useMemo,
ChangeEvent,
CSSProperties,
MouseEvent,
} from 'react';
function App() {
const [files, setFiles] = useState<File[]>([]);
const [startIndex, setStartIndex] = useState(0);
const [selectedFilenames, setSelectedFilenames] = useState<string[]>([]);
const imagesPerPage = 150;
const endIndex = useMemo(() => {
return Math.min(startIndex + imagesPerPage, files.length);
}, [files, startIndex]);
const displayedFiles = useMemo(() => {
return files.slice(startIndex, endIndex);
}, [files, startIndex]);
const handlePrevClick = () => {
if (startIndex - imagesPerPage >= 0) {
setStartIndex(startIndex - imagesPerPage);
}
};
const handleNextClick = () => {
if (endIndex < files.length) {
setStartIndex(startIndex + imagesPerPage);
}
};
const handleFileInputChange = (e: ChangeEvent<HTMLInputElement>) => {
const newFiles = Array.from(e.target.files!);
setFiles(newFiles);
};
const handleClickOnGridItem = (
i: number,
e: MouseEvent<HTMLDivElement, globalThis.MouseEvent>
) => {
if (e.ctrlKey || e.metaKey) {
setSelectedFilenames((prevSelectedFilenames) => {
if (prevSelectedFilenames.includes(displayedFiles[i].name)) {
return prevSelectedFilenames.filter(
(name) => name !== displayedFiles[i].name
);
} else {
return [...prevSelectedFilenames, displayedFiles[i].name];
}
});
} else if (e.shiftKey) {
if (selectedFilenames.length === 0) {
const range = [0, i].sort((a, b) => a - b);
const newSelectedFilenames = displayedFiles
.slice(range[0], range[1] + 1)
.map((file) => file.name);
setSelectedFilenames(newSelectedFilenames);
} else if (selectedFilenames.length > 0) {
const firstIndex = displayedFiles.findIndex(
(file) => file.name === selectedFilenames[0]
);
const range = [firstIndex, i].sort((a, b) => a - b);
const newSelectedFilenames = displayedFiles
.slice(range[0], range[1] + 1)
.map((file) => file.name);
setSelectedFilenames(newSelectedFilenames);
}
} else {
setSelectedFilenames([displayedFiles[i].name]);
}
};
return (
<div
style={{
display: 'flex',
flexDirection: 'column',
justifyContent: 'center',
alignItems: 'center',
}}
>
<input
type='file'
className='mt-3'
id='singleFileInput'
title='Upload an image'
accept='image/*'
multiple
onChange={handleFileInputChange}
/>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'evenly',
alignItems: 'center',
width: '25vw',
marginLeft: 'auto',
}}
>
<button onClick={handlePrevClick}> Prev </button>
<button onClick={handleNextClick}> Next </button>
</div>
<div
style={{
display: 'flex',
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center',
flexWrap: 'wrap',
}}
>
{displayedFiles.map((file, i) => {
const isSelected = selectedFilenames.includes(file.name);
const maxHeight = 80;
const margin = 2;
const imgStyle: CSSProperties = isSelected
? {
marginLeft: margin,
marginTop: margin,
maxHeight,
opacity: 1,
}
: {
marginLeft: margin,
marginTop: margin,
maxHeight,
opacity: 0.5,
};
return (
<img
style={imgStyle}
src={URL.createObjectURL(file)}
onClick={(e) => handleClickOnGridItem(i, e)}
/>
);
})}
</div>
</div>
);
}
export default App;
1条答案
按热度按时间lymnna711#
问题不在于
handleClickOnGridItem
函数的低效率。问题是,我在组件的return
范围内的每个渲染器上生成每个图像URL。现在,当用户上传文件时,我会生成图像URL列表,它现在工作得非常快,延迟为0。下面是超级高效的代码😅: