【发布时间】:2019-03-01 17:24:33
【问题描述】:
原帖
我有一个页面,它使用 Array.prototype.map 呈现包含在 redux 状态中的数组中的每个项目。该数组最初在调用 componentWillMount() 回调时填充,然后在用户请求更多数据时附加,即当用户滚动到页面底部并且有更多项目要获取时。我确保当使用 Array.prototype.concat() 附加数组时,redux 状态不会发生变化。然而,当更多的项目被添加到数组中时,看起来 react re 渲染了数组中的每个项目,而不仅仅是新项目,尽管为每个项目设置了一个唯一的键。我渲染组件的源代码如下:
this.props.state.list.map((item, index) => {
return (
<Grid className={classes.column} key={item._id} item xs={6} sm={6} md={4}>
<Card className={classes.card}>
<CardActionArea className={classes.cardAction}>
<div className={classes.cardWrapper}>
<div className={classes.outer}>
<div className={classes.bgWrapper}>
<img
className={[classes.bg, "lazyload"].join(" ")}
data-sizes="auto"
data-src={genSrc(item.pictures[0].file)}
data-srcset={genSrcSet(item.pictures[0].file)}
alt={item.name}
/>
</div>
</div>
<CardContent className={classes.cardContent}>
<div className={classes.textWrapper}>
<div className={classes.textContainer}>
<Typography
gutterBottom
className={[classes.text, classes.title].join(" ")}
variant="title"
color="inherit"
>
{item.name}
</Typography>
<Typography
className={[classes.text, classes.price].join(" ")}
variant="subheading"
color="inherit"
>
{item.minPrice === item.maxPrice
? `$${item.minPrice.toString()}`
: `$${item.minPrice
.toString()
.concat(" - $", item.maxPrice.toString())}`}
</Typography>
</div>
</div>
</CardContent>
</div>
</CardActionArea>
</Card>
</Grid>
);
});
这是我的减速器,如果你认为它可能与此有关:
import {
FETCH_PRODUCTS_PENDING,
FETCH_PRODUCTS_SUCCESS,
FETCH_PRODUCTS_FAILURE
} from "../actions/products";
let defaultState = {
list: [],
cursor: null,
calls: 0,
status: null,
errors: []
};
const reducer = (state = defaultState, action) => {
switch (action.type) {
case FETCH_PRODUCTS_PENDING:
return {
...state,
status: "PENDING"
};
case FETCH_PRODUCTS_SUCCESS:
return {
...state,
calls: state.calls + 1,
cursor: action.payload.data.cursor ? action.payload.data.cursor : null,
list: action.payload.data.results
? state.list.concat(action.payload.data.results)
: state.list,
status: "READY"
};
case FETCH_PRODUCTS_FAILURE:
return {
...state,
status: "FAIL",
call: state.calls + 1,
errors: [...state.errors, action.payload]
};
default:
return state;
}
};
export default reducer;
我从类似于我的问题的答案中读到,为从数组生成的每个组件设置一个 shouldComponentUpdate() 方法可能会起到作用,但没有明确的例子说明如何做到这一点。我非常感谢您的帮助。谢谢!
编辑 1
我还应该指定当新项目添加到数组中时,从数组呈现的组件完全卸载并重新安装在浏览器 DOM 中。理想情况下,我希望浏览器永远不要卸载现有组件,而只附加新渲染的组件。
编辑 2
这是呈现列表的组件的完整代码。
class Products extends React.Component {
componentDidMount() {
const { fetchNext, fetchStart, filter } = this.props
fetchStart(filter)
window.onscroll = () => {
const { errors, cursor, status } = this.props.state
if (errors.length > 0 || status === 'PENDING' || cursor === null) return
if (window.innerHeight + document.documentElement.scrollTop === document.documentElement.offsetHeight) {
fetchNext(filter, cursor)
}
}
}
render() {
const { classes, title } = this.props
return (
<div className={classes.container}>
<Grid container spacing={0}>
{
this.props.state.status && this.props.state.status === 'READY' && this.props.state.list &&
this.props.state.list.map((item, index) => {
return (
<Grid className={classes.column} key={item._id} item xs={6} sm={6} md={4}>
<Card className={classes.card}>
<CardActionArea className={classes.cardAction}>
<div className={classes.cardWrapper}>
<div className={classes.outer}>
<div className={classes.bgWrapper}>
<img className={[classes.bg, 'lazyload'].join(' ')} data-sizes='auto' data-src={genSrc(item.pictures[0].file)} data-srcset={genSrcSet(item.pictures[0].file)} alt={item.name} />
</div>
</div>
<CardContent className={classes.cardContent}>
<div className={classes.textWrapper}>
<div className={classes.textContainer}>
<Typography gutterBottom className={[classes.text, classes.title].join(' ')} variant="title" color='inherit'>
{item.name}
</Typography>
<Typography className={[classes.text, classes.price].join(' ')} variant='subheading' color='inherit'>
{item.minPrice === item.maxPrice ? `$${item.minPrice.toString()}` : `$${item.minPrice.toString().concat(' - $', item.maxPrice.toString())}`}
</Typography>
</div>
</div>
</CardContent>
</div>
</CardActionArea>
</Card>
</Grid>
)
})
}
</Grid>
</div>
)
}
}
【问题讨论】:
-
如果状态改变,一个组件和它的所有子组件都会重新渲染。这太正常了。但是,渲染并不意味着这些组件将再次卸载/安装或销毁/重新创建。这只会发生变化的部分。这与reconciliation 有关。所以,渲染并没有那么昂贵,DOM 操作才是。但是,如果您确定渲染所有这些组件会导致一些性能损失,那么您可以检查
shouldComponentUpdate(小心,有时触摸它会导致性能下降)或者您可以使用 PureComponent。 -
如果是这种情况,那么我应该了解更多信息:) 我认为在这种情况下只会发生重新渲染,而不是所有的 DOM 操作。我知道 React 会添加或删除子项,但我不认为在这种情况下所有 DOM 都会发生变化。
-
这也是我所期望的。我很确定我做错了什么。无论如何,再次感谢您的 cmets
-
当你说消失和重新出现时,你的意思是加载时从可见到消失吗?您是否对减速器上的状态字段做了任何会导致列表项不显示的事情?发布整个组件代码而不仅仅是列表渲染可能会有所帮助。
-
您可以写一个
pure Component来渲染您的地图项并将这些项作为道具传递给该组件,看看是否发生了同样的事情。更多参考reactjs.org/docs/optimizing-performance.html
标签: javascript reactjs redux react-redux rerender