web-dev-qa-db-fra.com

ReaL-native FlatList ne pas rendre de ligne lorsque les accessoires changent

J'ai un problème avec le nouveau composant FlatList. Plus précisément, il ne restitue pas ses lignes, même si les accessoires qui en dépendent dépendent des modifications. 


La documentation FlatList dit que:

Ceci est un PureComponent ce qui signifie qu'il ne sera pas rendu à nouveau si les accessoires restent peu profonds - égaux. Assurez-vous que tout votre renderItem fonction dépend est passé comme un accessoire qui n'est pas === après les mises à jour, sinon, votre interface utilisateur ne peut pas mettre à jour les modifications. Cela inclut les données prop et état du composant parent.

LA QUESTION

Cependant, étant donné que je modifie l'ID de l'élément selectedCategory - l'accessoire devant indiquer si la ligne est "sélectionnée" ou non -, je pense que les accessoires doivent être rendus. Est-ce que je me trompe?

J'ai vérifié les méthodes 'composantWillReceiveProps' des composants list et row, et la liste reçoit parfaitement la mise à jour, mais la méthode de cycle de vie de la ligne n'est jamais appelée. 

Si j'inclue une valeur d'état booléenne inutile et aléatoire dans le composant liste, et la bascule dans les deux sens lorsque les accessoires sont mis à jour, cela fonctionne - mais je ne sais pas pourquoi?

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

LE CODE

La structure de mon code est la suivante: j'ai un composant conteneur avec toute la logique et l'état, qui contient un composant FlatList (présentation, aucun état), qui contient à nouveau une ligne de présentation personnalisée.

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

Le conteneur comprend ce composant:

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

Liste customisée:

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

}

(les données proviennent du relais)

Enfin la rangée:

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

La ligne n’est pas très intéressante, mais je l’ai incluse pour montrer qu’elle est entièrement apatride et dépendante des accessoires de ses parents. 

L'état est mis à jour comme suit:

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

L'état ressemble à ceci:

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

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

18
jhm

Le problème ici réside dans le fait que

  1. Vous effectuez la mutation d'une tranche d'état existante au lieu de créer une copie mutée

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

Cela devrait être

_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. Les accessoires de FlatList restent les mêmes, votre fonction _renderRow peut s’appuyer sur le paramètre selectedCategory qui change (si ce n’est pas la première erreur), mais le composant FlatList ne le sait pas. Pour résoudre ce problème, utilisez le extraData prop.

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

Vous pouvez simplement résoudre ce problème en passant props à extraData in liste plate composant comme celui-ci,

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

Je suis d'accord avec Nimelrian. De plus, si votre état est un tableau, vous pouvez créer un objet tableau à partir de l'état en procédant comme suit:

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

Ensuite, utilisez la méthode .Push () pour y ajouter votre nouvel objet comme ceci:

oldReportCopy.Push(selectedCategory);

vous pouvez ensuite remettre ce nouvel objet Array à l'état suivant:

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