避免在 React Hooks 中重新渲染列表
Avoid rerendering a list in React Hooks
我有一个图像列表,这些图像通过 map
函数在页面加载时呈现。我希望能够 select 一张或多张图片,并突出显示它们。此外,selected 图片的标题将显示在页面顶部。我 运行 遇到的问题是每次我 select 图像时列表似乎都会重新呈现。这是组件:
export default function App() {
const [images, setImages] = React.useState<Image[]>([]);
const [selected, setSelected] = React.useState<boolean>(false);
const [imageTitles, setImageTitles] = React.useState<string[]>([]);
const handleImageSelection = (title: string, index: number) => {
setSelected(!selected);
const imageExists = imageTitles.indexOf(title) !== -1
if (!imageExists) {
setImageTitles([...imageTitles, title]);
} else {
setImageTitles(imageTitles.filter(str => str !== title));
}
}
return (
<div>
{console.log(images)} // The images array renders every time
<div>{imageTitles.map(title => <p>{title}</p>)}</div>
<section className="images">{
images.map(({ title, image}, index) =>
<img
style={{
marginBottom: 4,
border: imageTitles.indexOf(title) !== -1 ?
"4px solid blue" : "", // A selected image is highlighted
borderRadius: 16
}}
src={image}
alt={image}
onClick={() => handleImageSelection(title, index)}
/>
)
}</section>
</div>
);
}
我想知道是不是因为每次图像 selected/unselected 时我都在更改 imageTitles
数组的大小(因此 index
值)。
我也这样试过useCallback
:
const handleImageSelection = React.useCallback((title: string, index: number) => {
setSelected(!selected);
const imageExists = imageTitles.indexOf(title) !== -1
if (!imageExists) {
setImageTitles([...imageTitles, title]);
} else {
setImageTitles(imageTitles.filter(str => str !== title));
}
}, [selected, imageTitles])
但这似乎并没有奏效。我的猜测是因为 imagetitles
每次都会改变。
那么,是否有可能(出于性能原因)避免每次图像 selected/unselected 时都重新渲染图像列表?
由于您在 useCallback 的依赖项中添加了 selected 和 imageTitles,因此每次调用时都会重新创建 useCallback,因为它本身设置了 selected 和 imageTitles 状态
这里的解决方案是使用 setState 回调模式并将空数组作为依赖项传递给 useCallback
const handleImageSelection = React.useCallback((title: string, index: number) => {
setSelected(prevSelected => !prevSelected);
setImageTitles(prevImageTitles => {
const imageExists = prevImageTitles.indexOf(title) !== -1;
if (!imageExists) {
return [...prevImageTitles, title];
} else {
return prevImageTitles.filter(str => str !== title));
}
});
}, []);
另请注意,每次您设置正常行为的状态时,列表都会重新呈现。但是 React 会优化渲染,只会重新渲染那些需要更改的元素。
这里要注意的另一件事是,您必须将 key prop 传递给映射值,以便 React 进一步优化重新渲染
return (
<div>
<div>{imageTitles.map(title => <p key={title}>{title}</p>)}</div>
<section className="images">{
images.map(({ title, image}, index) =>
<img
key={title} // any unique value here. If you don't have anything use index
style={{
marginBottom: 4,
border: imageTitles.indexOf(title) !== -1 ?
"4px solid blue" : "", // A selected image is highlighted
borderRadius: 16
}}
src={image}
alt={image}
onClick={() => handleImageSelection(title, index)}
/>
)
}</section>
</div>
);
我有一个图像列表,这些图像通过 map
函数在页面加载时呈现。我希望能够 select 一张或多张图片,并突出显示它们。此外,selected 图片的标题将显示在页面顶部。我 运行 遇到的问题是每次我 select 图像时列表似乎都会重新呈现。这是组件:
export default function App() {
const [images, setImages] = React.useState<Image[]>([]);
const [selected, setSelected] = React.useState<boolean>(false);
const [imageTitles, setImageTitles] = React.useState<string[]>([]);
const handleImageSelection = (title: string, index: number) => {
setSelected(!selected);
const imageExists = imageTitles.indexOf(title) !== -1
if (!imageExists) {
setImageTitles([...imageTitles, title]);
} else {
setImageTitles(imageTitles.filter(str => str !== title));
}
}
return (
<div>
{console.log(images)} // The images array renders every time
<div>{imageTitles.map(title => <p>{title}</p>)}</div>
<section className="images">{
images.map(({ title, image}, index) =>
<img
style={{
marginBottom: 4,
border: imageTitles.indexOf(title) !== -1 ?
"4px solid blue" : "", // A selected image is highlighted
borderRadius: 16
}}
src={image}
alt={image}
onClick={() => handleImageSelection(title, index)}
/>
)
}</section>
</div>
);
}
我想知道是不是因为每次图像 selected/unselected 时我都在更改 imageTitles
数组的大小(因此 index
值)。
我也这样试过useCallback
:
const handleImageSelection = React.useCallback((title: string, index: number) => {
setSelected(!selected);
const imageExists = imageTitles.indexOf(title) !== -1
if (!imageExists) {
setImageTitles([...imageTitles, title]);
} else {
setImageTitles(imageTitles.filter(str => str !== title));
}
}, [selected, imageTitles])
但这似乎并没有奏效。我的猜测是因为 imagetitles
每次都会改变。
那么,是否有可能(出于性能原因)避免每次图像 selected/unselected 时都重新渲染图像列表?
由于您在 useCallback 的依赖项中添加了 selected 和 imageTitles,因此每次调用时都会重新创建 useCallback,因为它本身设置了 selected 和 imageTitles 状态
这里的解决方案是使用 setState 回调模式并将空数组作为依赖项传递给 useCallback
const handleImageSelection = React.useCallback((title: string, index: number) => {
setSelected(prevSelected => !prevSelected);
setImageTitles(prevImageTitles => {
const imageExists = prevImageTitles.indexOf(title) !== -1;
if (!imageExists) {
return [...prevImageTitles, title];
} else {
return prevImageTitles.filter(str => str !== title));
}
});
}, []);
另请注意,每次您设置正常行为的状态时,列表都会重新呈现。但是 React 会优化渲染,只会重新渲染那些需要更改的元素。
这里要注意的另一件事是,您必须将 key prop 传递给映射值,以便 React 进一步优化重新渲染
return (
<div>
<div>{imageTitles.map(title => <p key={title}>{title}</p>)}</div>
<section className="images">{
images.map(({ title, image}, index) =>
<img
key={title} // any unique value here. If you don't have anything use index
style={{
marginBottom: 4,
border: imageTitles.indexOf(title) !== -1 ?
"4px solid blue" : "", // A selected image is highlighted
borderRadius: 16
}}
src={image}
alt={image}
onClick={() => handleImageSelection(title, index)}
/>
)
}</section>
</div>
);