Estoy tratando de filtrar mi lista de objetos de matriz y luego intento mostrar en ListView con el nuevo DataSource. Sin embargo, la lista no se está filtrando. Sé que mi función de filtro funciona correctamente. (Lo revisé en console.log)

Estoy usando Redux para asignar mi estado a la utilería. Y luego tratando de filtrar el accesorio. ¿Es este el camino equivocado?

Aquí está mi código:

/*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é estoy haciendo mal aquí?

5
Sankalp Singha 7 mar. 2017 a las 12:09

2 respuestas

La mejor respuesta

Es un poco difícil seguir lo que está pasando aquí. Lo simplificaría para que sea así:

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

Algunos puntos:

  1. Su componente tiene estado. Hay una parte del estado que pertenece solo al componente: el término de búsqueda. Mantenga eso en estado de componente.
  2. No cambie la fuente de datos en las funciones del ciclo de vida. Hazlo en el último punto que sabes que es necesario: en render.
  3. Supongo que hay algo asíncrono en assetFetch, por lo que probablemente deberías devolverlo en componentDidMount.
  4. Cambié de componentWillMount a componentDidMount. Se recomienda poner la búsqueda asíncrona componentDidMount. Esto puede importar si alguna vez haces renderizado del lado del servidor.
  5. Omita el filtrado si no hay un término de búsqueda. Esto solo importaría si la lista es muy grande.

Una cosa que me preocupa un poco es el patrón de buscar dentro de un componente, ponerlo en un estado global y luego confiar en ese componente para reaccionar al cambio de estado global. Por lo tanto, cambiar el estado global se convierte en un efecto secundario de simplemente ver algo. Supongo que lo está haciendo porque assets se usa en otro lugar, y este es un punto conveniente para actualizarlos del servidor para que aparezcan en otros componentes que no los recuperen. Este patrón puede dar lugar a errores difíciles de encontrar.

8
Tim Scott 23 mar. 2017 a las 15:00

Debes hacer setState para activar el render. Así es como lo haría:

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 7 mar. 2017 a las 10:44