React повторно отображает все элементы в массиве при добавлении новых элементов, несмотря на использование уникальных ключей для всех элементов - PullRequest
0 голосов
/ 26 сентября 2018

Исходное сообщение

У меня есть страница, которая отображает каждый элемент в массиве, содержащемся в состоянии избыточности, используя 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 браузера при добавлении новых элементов в массив.В идеале я бы хотел, чтобы браузер никогда не отключал существующие компоненты и только добавлял только что созданные компоненты.

Edit 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>
    )
  }
}

1 Ответ

0 голосов
/ 26 сентября 2018

Получается, что это был условный рендер, который приводил к исчезновению списка рендеринга.В моем случае я установил его так, чтобы компонент отображал список только в том случае, если все текущие операции извлечения были завершены, поэтому, конечно, список исчезнет между вызовами извлечения.

Изменение следующего значения из

    {
      this.props.state.status && this.props.state.status === 'READY' && this.props.state.list &&
        this.props.state.list.map((item, index) => {
          return <Item />
        }
    }

к этому, тем самым сняв проверку на состояние завершенной выборки, решил мою проблему.

    {
      this.props.state.list &&
        this.props.state.list.map((item, index) => {
          return <Item />
        }
    }
...