I'm trying to design something like the below screenshot in react native. Please note each tile is a Product element fetched from the backend.
But I'm unable to do this using a ListView and its renderRow method which is denying me to use any kind of InfiniteScroll Components.
Currently I'm running a loop with 2 elements at a time and rendering 2 elements inside a scroll view. Below is my Code to explain better.
render() {
var elem = [];
for(var i = 0; i < this.state.products.length; i+=2) {
var prod = this.state.products[i];
var prod2 = this.state.products[i + 1];
elem.push(
<View style={styles.view} key={i} >
<ProductTile onPressAction={this._pdpPage} prod={prod} index={i} />
<ProductTile onPressAction={this._pdpPage} prod={prod2} index={i + 1} />
</View>
);
}
return (
<ScrollView>
{elem}
</ScrollView>
)
}
And then based on the index prop I'm aligning the elements on left or right. My View style looks like below:
view: {
flex: 1,
flexDirection: 'row',
},
Please suggest a better way to do this.
Thanks in advance.
A good way we have done this in production in the past, and it has worked out well, is to get the width of the container and set the width of the cards to 50% of the width, then you can just push all of the single elements into the listview. Also, be sure to set up a flexWrap
of wrap
.
This will work across all device sizes, and requires not additional modules or libraries.
Check out the sample code below and example here:
https://rnplay.org/apps/t_6-Ag
/* Get width of window */
const width = Dimensions.get('window').width
/* ListView */
<ListView
contentContainerStyle={styles.listView}
dataSource={this.state.dataSource}
renderRow={this.renderRow.bind(this)}
/>
/* Row */
renderRow () {
return <View style={styles.card}>
<Text>{rowData.name} {rowData.price}</Text>
</View>
/* Styles */
listView: {
flexDirection: 'row',
flexWrap: 'wrap'
},
card: {
backgroundColor: 'red',
width: (width / 2) - 15,
height: 300,
marginLeft: 10,
marginTop: 10
}
React Native has a good example of this in their CameraRollView.js
example. They use a library called groupByEveryN
to allow you to set how many items to render per row.
Notice the change to how you initiate the ListView.DataSource
...
var ds = new ListView.DataSource({rowHasChanged: this._rowHasChanged});
this.state.dataSource = ds.cloneWithRows(
groupByEveryN(this.state.assets, this.props.imagesPerRow)
);
Your renderRow
function just needs to expect an array of items...
// rowData is an array of images
_renderRow: function(rowData: Array<Image>, sectionID: string, rowID: string) {
var images = rowData.map((image) => {
if (image === null) {
return null;
}
return this.props.renderImage(image);
});
return (
<View style={styles.row}>
{images}
</View>
);
}
Full example file here: https://github.com/facebook/react-native/blob/a564af853f108e071e941ac08be4cde864f5bfae/Examples/UIExplorer/js/CameraRollView.js.
Using https://facebook.github.io/react-native/docs/flexbox as a baseline I've been able to easily do this in my projects by adding flexWrap: 'wrap' as below.
`<View style={{flex: 1, flexDirection: 'row', flexWrap: 'wrap'}}>
<View style={{height: 150, width: '50%', backgroundColor: 'powderblue'}} />
<View style={{height: 150, width: '50%', backgroundColor: 'skyblue'}} />
<View style={{height: 150, width: '50%', backgroundColor: 'steelblue'}} />
<View style={{height: 150, width: '50%', backgroundColor: 'powderblue'}} />
<View style={{height: 150, width: '50%', backgroundColor: 'skyblue'}} />
<View style={{height: 150, width: '50%', backgroundColor: 'steelblue'}} />
</View>`
So in your project
render() {
var elem = [];
for(var i = 0; i < this.state.products.length; i+=2) {
var prod = this.state.products[i];
var prod2 = this.state.products[i + 1];
elem.push(
<View style={styles.view} key={i} >
<ProductTile onPressAction={this._pdpPage} prod={prod} index={i} />
<ProductTile onPressAction={this._pdpPage} prod={prod2} index={i + 1} />
</View>
);
}
return (
<ScrollView>
<View style={{flex: 1, flexDirection: 'row', flexWrap: 'wrap'}}>
{elem}
</View>
</ScrollView>
)
}
view: {
width: '50%',
height: 150,
},
As far as height goes, it needs to be set but may work with 'auto' if you don't have a set height. I didn't test otherwise.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With