Исходное сообщение
У меня есть страница, которая отображает каждый элемент в массиве, содержащемся в состоянии редукции, с использованием Array.prototype.map. Массив изначально заполняется при вызове обратного вызова componentWillMount(), а затем добавляется, когда пользователь запрашивает дополнительные данные, т. е. когда пользователь прокручивает страницу до конца и если есть еще элементы для выборки. Я слежу за тем, чтобы состояние редукции не изменялось при добавлении массива с помощью Array.prototype.concat(). Однако, когда в массив добавляется больше элементов, кажется, что реакция повторно отображает каждый элемент в массиве, а не только новые элементы, несмотря на установку уникального ключа для каждого элемента. Мой исходный код для рендеринга компонентов выглядит следующим образом:
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>
)
}
}
shouldComponentUpdate
(будьте осторожны, иногда прикосновение к нему приводит к снижению производительности) или вы можете использовать PureComponent. - person devserkan   schedule 26.09.2018pure Component
, чтобы просто отображать элементы карты и передавать элементы в качестве реквизита этому компоненту и смотреть, происходит ли то же самое. для получения дополнительной информации reactjs.org/docs/optimizing-performance.html - person aravind_reddy   schedule 26.09.2018this.props.state.status && this.props.state.status === 'READY'
. Кажется, вы можете полностью удалить эту проверку, и всплывающее окно при загрузке должно быть исправлено. - person TLadd   schedule 26.09.2018Item
, но просто помните, чтоpure component
выполняет толькоshallow comparison
, если у вас есть вложенные объекты, вы должны попытаться реализовать componentShouldUpdate самостоятельно. если вы понимаете, что я имею в виду - person aravind_reddy   schedule 26.09.2018