How to filter data in ListView React-native?
It's a little hard to follow what's going on here. I would simplify it to be like so:
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);
A few points:
- Your component is stateful. There is one piece of state that belongs only to the component: the search term. Keep that in component state.
- Don't change the data source in life cycle functions. Do it the latest point you know it's needed: in render.
- I'm guessing that there's something async in
assetFetch
, so you probably should return it incomponentDidMount
. - I changed from
componentWillMount
tocomponentDidMount
. It's recommended to put async fetchingcomponentDidMount
. This can matter if you ever do server side rendering. - Skip filtering if there is no search term. This would only matter if the list is very large.
One thing I have a little concern with is the pattern of fetching inside a component, putting it in global state, and then relying on that component to react to the global state change. Thus changing global state becomes a side effect of simply viewing something. I assume you are doing it because assets
is used elsewhere, and this is a convenient point to freshen them from the server so that they will show up in other components that do not fetch them. This pattern can result in hard-to-find bugs.
Sankalp Singha
I am a professional Musician(Guitarist)/Composer/Sound Engineer and a passionate Programmer. I love Rails and the FrontEnd Stack of JS/CSS and HTML. I simply love Web Development. I am also an avid Network Security and Web Application Security Analyst. My strong areas of programming include : Rails, VueJs, Nginx, Ansible, PHP, HTML5, CSS3, Jquery/JS I am always tinkering and rejuvenating myself with the new tech stuff on the block and always trying to make or break stuff. In my free time you can catch me recording in my home studio. :)
Updated on June 11, 2022Comments
-
Sankalp Singha almost 2 years
I am trying to filter my array object list and then trying to display in the ListView with new DataSource. However, the list is not getting filtered. I know that my filter function works correctly. ( I checked it in the console.log )
I am using Redux to map my state to prop. And then trying to filter the prop. Is this the wrong way?
Here is my 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);
What am I doing wrong here?