web-dev-qa-db-ja.com

React-native FlatListは、小道具が変更されたときに行をレンダリングしません

新しいFlatListコンポーネントに問題があります。具体的には、行が変更に依存していることを支持している場合でも、行を再レンダリングしません。


FlatListのドキュメントには次のように書かれています:

これはPureComponentです。つまり、小道具が浅いままの場合は再レンダリングされません。 renderItem関数が依存するすべてのものが、更新後===ではないpropとして渡されることを確認してください。そうでない場合、UIは変更時に更新されない可能性があります。これには、データプロパティと親コンポーネントの状態が含まれます。

質問

ただし、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}
 />

CustomList:

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

}

(データはリレーから取得されます)

最後に行:

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: '',
  },
  ...
};

28
jhm

ここでの問題は、

  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の小道具は同じままです。_renderRow関数は、変更するselectedCategory小道具に依存する場合があります(最初の間違いでない場合)が、FlatListコンポーネントはそれを認識しません。これを解決するには、 extraData propを使用します。

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

props to extraData in flat listコンポーネントをこのように渡すだけで、この問題を解決できます。

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

ニメリアンに同意します。また、状態が配列の場合、次の操作を行うことで状態から配列オブジェクトを作成できます。

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

次に、.Push()メソッドを使用して、次のように新しいオブジェクトを追加します。

oldReportCopy.Push(selectedCategory);

その後、この新しい配列オブジェクトを状態に戻すことができます。

this.setState({ report: oldReportCopy });
2
Abiodun Adenle

他の人には当てはまらないかもしれませんが、FlatListによってレンダリングされているアイテムの配列が空になったときだけ問題が発生していることに気付きました。私の場合は、FlatListをまったくレンダリングせず、代わりに別のビューをレンダリングする必要がありました。当然、「再レンダリングしない」という問題が修正されました。

0
stevejboyer