尽管为所有项目使用了唯一键,但是当添加新项目时,React re会以数组形式呈现所有项目

时间:2018-09-26 02:36:11

标签: javascript reactjs redux react-redux rerender

原始帖子

我有一个页面,该页面使用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>
    )
  }
}

1 个答案:

答案 0 :(得分:1)

因此,事实证明是有条件的渲染导致渲染列表消失。就我而言,我将其设置为仅在所有正在进行的提取操作均已完成的情况下才呈现列表,因此该列表当然会在两次调用之间消失。

更改以下内容

    {
      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 />
        }
    }