web-dev-qa-db-fra.com

Comment filtrer les données dans ListView React-native?

J'essaye de filtrer ma liste d'objets de tableau, puis j'essaie de l'afficher dans ListView avec le nouveau DataSource. Cependant, la liste n'est pas filtrée. Je sais que ma fonction de filtre fonctionne correctement. (Je l'ai vérifié dans le fichier console.log)

J'utilise Redux pour mapper mon état pour soutenir. Et puis en essayant de filtrer l'accessoire. Est-ce la mauvaise façon?

Voici mon code:

/*global fetch:false*/
import _ from 'lodash';
import React, { Component } from 'react';
import { ListView, Text as NText } from 'react-native';
import { connect } from 'react-redux';
import { Actions } from 'react-native-router-flux';
import {
  Container, Header, Item,
  Icon, Input, ListItem, Text,
  Left, Right, Body, Button
} from 'native-base';


import Spinner from '../common/Spinner';
import HealthImage from '../misc/HealthImage';
import { assetsFetch } from '../../actions';

const ds = new ListView.DataSource({
  rowHasChanged: (r1, r2) => r1 !== r2
});

class AssetsList extends Component {
  componentWillMount() {
    this.props.assetsFetch();

    // Implementing the datasource for the list View
    this.createDataSource(this.props.assets);
  }

  componentWillReceiveProps(nextProps) {
    // Next props is the next set of props that this component will be rendered with.
    // this.props is still equal to the old set of props.
    this.createDataSource(nextProps.assets);
  }

  onSearchChange(text) {
    const filteredAssets = this.props.assets.filter(
      (asset) => {
        return asset.name.indexOf(text) !== -1;
      }
    );

    this.dataSource = ds.cloneWithRows(_.values(filteredAssets));
  }

  createDataSource(assets) {
    this.dataSource = ds.cloneWithRows(assets);
  }


  renderRow(asset) {
    return (
      <ListItem thumbnail>
          <Left>
              <HealthImage health={asset.health} />
          </Left>
          <Body>
              <Text>{asset.name}</Text>

              <NText style={styles.nText}>
                Location: {asset.location} |
                Serial: {asset.serial_number}
              </NText>
              <NText>
                Total Samples: {asset.total_samples}
              </NText>

          </Body>
          <Right>
              <Button transparent onPress={() => Actions.assetShow()}>
                  <Text>View</Text>
              </Button>
          </Right>
      </ListItem>
    );
  }



  render() {
    return (
     <Input
                  placeholder="Search"
                  onChangeText={this.onSearchChange.bind(this)}
                />
        <ListView
          enableEmptySections
          dataSource={this.dataSource}
          renderRow={this.renderRow}
        />


    );
  }
}

const mapStateToProps = state => {
  return {
    assets: _.values(state.assets.asset),
    spinner: state.assets.asset_spinner
  };
};

export default connect(mapStateToProps, { assetsFetch })(AssetsList);

Qu'est-ce que je fais mal ici?

5
Sankalp Singha

C'est un peu difficile de suivre ce qui se passe ici. Je voudrais simplifier pour être comme suit:

class AssetsList extends Component {
  state = {};

  componentDidMount() {
    return this.props.assetsFetch();
  }

  onSearchChange(text) {
    this.setState({
      searchTerm: text
    });
  }

  renderRow(asset) {
    return (
      <ListItem thumbnail>
          <Left>
              <HealthImage health={asset.health} />
          </Left>
          <Body>
              <Text>{asset.name}</Text>

              <NText style={styles.nText}>
                Location: {asset.location} |
                Serial: {asset.serial_number}
              </NText>
              <NText>
                Total Samples: {asset.total_samples}
              </NText>

          </Body>
          <Right>
              <Button transparent onPress={() => Actions.assetShow()}>
                  <Text>View</Text>
              </Button>
          </Right>
      </ListItem>
    );
  }

  getFilteredAssets() {

  }

  render() {
    const filteredAssets = this.state.searchTerm
      ? this.props.assets.filter(asset => {
          return asset.name.indexOf(this.state.searchTerm) > -1;
        })
      : this.props.assets;
    const dataSource = ds.cloneWithRows(filteredAssets);
    return (
     <Input
                  placeholder="Search"
                  onChangeText={this.onSearchChange.bind(this)}
                />
        <ListView
          enableEmptySections
          dataSource={dataSource}
          renderRow={this.renderRow}
        />
    );
  }
}

const mapStateToProps = state => {
  return {
    assets: _.values(state.assets.asset),
    spinner: state.assets.asset_spinner
  };
};

export default connect(mapStateToProps, { assetsFetch })(AssetsList);

Quelques points:

  1. Votre composant est dynamique. Il existe un élément d'état qui n'appartient qu'au composant: le terme de recherche. Gardez cela dans l'état du composant.
  2. Ne changez pas la source de données dans les fonctions de cycle de vie. Faites le dernier point dont vous savez qu'il est nécessaire: dans le rendu.
  3. Je suppose qu'il y a quelque chose d'async dans assetFetch, vous devriez donc probablement le renvoyer dans componentDidMount.
  4. J'ai changé de componentWillMount à componentDidMount. Il est recommandé de mettre l'extraction asynchrone componentDidMount. Cela peut être important si vous effectuez un rendu côté serveur.
  5. Ignorer le filtrage s'il n'y a pas de terme de recherche. Cela n’aurait d’importance que si la liste est très longue.

Une chose qui me préoccupe un peu est le mode de récupération dans un composant, de le placer dans un état global, puis de compter sur ce composant pour réagir au changement d'état global. Ainsi, le changement d'état global devient un effet secondaire du simple fait de regarder quelque chose. Je suppose que vous le faites parce que assets est utilisé ailleurs et qu’il est pratique de les actualiser depuis le serveur afin qu’ils apparaissent dans d’autres composants qui ne les récupèrent pas. Ce modèle peut entraîner des bogues difficiles à trouver.

6
Tim Scott

Vous devez faire setState pour déclencher le rendu. Voici comment je le ferais -

constructor(props) {
  super(props);
  this.ds = new ListView.DataSource({ rowHasChanged: (r1,r2) => r1 !== r2 });
  this.state = {
    assets: []
  };
}

componentWillMount() {
  this.props.assetsFetch();
  this.setState({
    assets: this.props.assets
  });
}

componentWillReceiveProps(nextProps) {
  this.setState({
    assets: nextProps.assets
  });
}

onSearchChange(text) {
  const filteredAssets = this.props.assets.filter(asset => asset.name.indexOf(text) !== -1);
  this.setState({
    assets: _.values(filteredAssets)
  });
}


render() {
  ...
  <ListView
    dataSource={this.ds.cloneWithRows(this.state.assets)}
    .....
  />
}
0
vinayr