【问题标题】:React-native FlatList not rerendering row when props change当道具更改时,React-native FlatList 不会重新渲染行
【发布时间】:2017-05-31 07:37:16
【问题描述】:

我在使用新的 FlatList 组件时遇到问题。具体来说,它不会重新呈现它的行,即使该行依赖于更改的道具。


FlatList 文档说:

这是一个 PureComponent,这意味着它不会重新渲染,如果 道具保持浅平。确保所有你的 renderItem 函数取决于在更新后作为不是 === 的道具传递, 否则您的 UI 可能不会在更改时更新。这包括数据 prop 和父组件状态。

问题

但是,当我更改 selectedCategory 项目的 ID 时 - 应该指示该行是否被“选中”的道具 - 我相信道具应该重新呈现。我弄错了吗?

我检查了列表和行组件的 'componentWillReceiveProps' 方法,列表接收更新很好,但从未调用行的生命周期方法。

如果我在列表组件中包含一个随机的、无用的布尔状态值,并在道具更新时来回切换它,它可以工作 - 但我不知道为什么?

state = { updated: false };

componentWillReceiveProps(nextProps) {
  this.setState(oldstate => ({
    updated: !oldstate.updated,
  }));
}

<FlatList
  data={this.props.items.allAnimalCategories.edges}
  renderItem={this._renderRow}
  horizontal={true}
  keyExtractor={(item, index) => item.node.id}
  randomUpdateProp={this.state.updated}
/>

代码

我的代码结构是这样的:我有一个包含所有逻辑和状态的容器组件,其中包含一个 FlatList 组件(展示,无状态),它又包含一个自定义展示行。

Container
  Custom list component that includes the FlatList component
  (presentational, stateless) and the renderRow method
    Custom row (presentational, stateless)

容器包含这个组件:

 <CustomList
   items={this.props.viewer}
   onCategoryChosen={this._onCategoryChosen}
   selectedCategory={this.state.report.selectedCategory}
 />

自定义列表:

class CustomList extends Component {
  _renderRow = ({ item }) => {
    return (
      <CustomListRow
        item={item.node}
        selectedCategory={this.props.selectedCategory}
        onPressItem={this.props.onCategoryChosen}
      />
    );
  };

  render() {
    return (
      <View style={_styles.container}>
        <FlatList
          data={this.props.items.categories.edges}
          renderItem={this._renderRow}
          horizontal={true}
          keyExtractor={(item, index) => item.node.id}
          randomUpdateProp={this.state.updated}
        />
      </View>
    );
  }

}

(数据来自Relay)

最后一行:

render() {
    const idsMatch = this.props.selectedCategory.id == this.props.item.id;
    return (
      <TouchableHighlight onPress={this._onItemPressed}>
        <View style={_styles.root}>
          <View style={[
              _styles.container,
              { backgroundColor: this._getBackgroundColor() },
            ]}>
            {idsMatch &&
              <Image
                style={_styles.icon}
                source={require('./../../res/img/asd.png')}
              />}
            {!idsMatch &&
              <Image
                style={_styles.icon}
                source={require('./../../res/img/dsa.png')}
              />}
            <Text style={_styles.text}>
              {capitalizeFirstLetter(this.props.item.name)}
            </Text>
          </View>
          <View style={_styles.bottomView}>
            <View style={_styles.greyLine} />
          </View>
        </View>
      </TouchableHighlight>
    );
  }

该行并不那么有趣,但我将其包括在内以表明它完全是无状态的并且依赖于它的父母道具。

状态更新如下:

_onCategoryChosen = category => {
    var oldReportCopy = this.state.report;
    oldReportCopy.selectedCategory = category;
    this.setState(Object.assign({}, this.state, { report: oldReportCopy }));
  };

状态如下所示:

state = {
    ...
    report: defaultStateReport,
  };

const defaultStateReport = {
  selectedCategory: {
    id: 'some-long-od',
    name: '',
  },
  ...
};

【问题讨论】:

  • 您对文档的引用解决了我的问题。

标签: react-native react-native-listview


【解决方案1】:

这里的问题在于

  1. 您正在更改现有的状态切片,而不是创建变异副本

_onCategoryChosen = category => {
    var oldReportCopy = this.state.report; // This does not create a copy!
    oldReportCopy.selectedCategory = category;
    this.setState(Object.assign({}, this.state, { report: oldReportCopy }));
};

这应该是

_onCategoryChosen = category => {
    var oldReportCopy = Object.assign({}, this.state.report);
    oldReportCopy.selectedCategory = category;
    // setState handles partial updates just fine, no need to create a copy
    this.setState({ report: oldReportCopy });
};

  1. FlatList 的 props 保持不变,您的 _renderRow 函数可能依赖于确实发生变化的 selectedCategory prop (如果不是因为第一个错误),但 FlatList 组件不知道这一点。要解决这个问题,请使用 extraData 属性。

     <FlatList
       data={this.props.items.categories.edges}
       renderItem={this._renderRow}
       horizontal={true}
       keyExtractor={(item, index) => item.node.id}
       extraData={this.props.selectedCategory}
     />
    

【讨论】:

  • 非常感谢,这非常有效。回想起来,extraData 属性是使用 FlatLists 时非常重要且可能经常使用的工具
  • 天哪,谢谢你:extraData ...。我尝试刷新已删除的项目大约 2 小时但没有成功:D 但这有帮助;/ 这里有一些奇怪的东西。
  • 你关于 extraData 的线索,,,救了我的命@Ernestyno 非常感谢
  • 非常感谢,extraData 道具对我来说就像魅力一样
  • 天哪,非常感谢!我刚刚从类组件转移到功能组件,我认为这就是我在 useState 上做错的原因......
【解决方案2】:

您可以像这样在 flat list 组件中将 props 传递给 extraData 来解决这个问题,

  <FlatList
    data={this.props.data}
    extraData={this.props}
    keyExtractor={this._keyExtractor}
    renderItem={this._renderItem}
  />

【讨论】:

    【解决方案3】:

    就我而言,我在使用keyExtractor时犯了一个简单的错误

    我变了

    keyExtractor={(item, index) => index.toString()} 
    

    keyExtractor={(item, index) => item.key} 
    

    我在过滤列表后看到了一些奇怪的效果,其中过滤出的组件的道具被渲染以代替新组件的道具,我的直觉是,因为我使用的是数组的索引而不是唯一键,所以我是即使基础组件实际上正在更改,也只是将旧道具传递给新组件。

    【讨论】:

    • 当我意识到自己在做什么时,我从来没有这么用力过自己。不要使用索引:)
    • 永远不要再使用索引,它对我帮助很大。谢谢!
    【解决方案4】:

    我同意 Nimelrian。此外,如果您的状态是一个数组,您可以通过执行以下操作从该状态创建一个数组对象:

     var oldReportCopy = Object.assign([], this.state.report);
    

    然后使用 .push() 方法将新对象添加到其中,如下所示:

    oldReportCopy.push(selectedCategory);
    

    然后您可以将这个新的数组对象设置回状态:

    this.setState({ report: oldReportCopy });
    

    【讨论】:

      【解决方案5】:

      也许其他任何人都不会出现这种情况,但我意识到我只是在 FlatList 呈现的项目数组变空时遇到了麻烦。在我的情况下,我只需要根本不渲染 FlatList,而是在其位置渲染不同的 View,这当然解决了我的问题,即“不重新渲染”。

      【讨论】:

        【解决方案6】:

        看第 4 行

        _onCategoryChosen = category => {
            var oldReportCopy = this.state.report;
            oldReportCopy.selectedCategory = category;
            this.setState({ report: [...oldReportCopy] }); // Notice this line
          };
        

        【讨论】:

          【解决方案7】:

          这对我不起作用

                 setTabData(tabD);
          

          这对我有用

                setTabData([...tabD]);
          

          【讨论】:

            【解决方案8】:

            在反应钩子中你可以做这样的事情:

            const onPressLeaderSelect = (item, index) => {
                let oldMemberCopy = Object.assign([], teamMemberArr);  //'teamMemberArr' is local state
                let objIndex = oldMemberCopy.findIndex((obj => obj.teamLeader == 1));
                oldMemberCopy[objIndex].teamLeader = 0
                oldMemberCopy[index].teamLeader = 1
                console.log('onPressLeaderSelect', oldMemberCopy)
                setteamMemberArr(oldMemberCopy)
            }
            

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 2018-12-27
              • 2018-12-27
              • 1970-01-01
              • 1970-01-01
              • 2018-07-01
              • 1970-01-01
              相关资源
              最近更新 更多