Skip to content

Commit 70427a8

Browse files
jukbenferrannp
authored andcommitted
feat: GridView: ListView upgraded to VirtualizedList (callstack#150)
* feat(gridView): use VirtualizedList * feat(gridView): orientation should work. start with public API. * feat(gridView): polished code, added public API * fix(gridView): removed comments which broke the CI * docs(gridView): updated documentation * refactor(gridView): better distinguish between private / public method in the example * docs(gridView): fixed comments * fix(gridView): round the layout width * chore(docs): fixed docs generation. When you use flow you have to use named arguments of function * refactor(gridView): renderItem shoud return valid react element * BREAKING: migrate GridView to use VirtualizedList * fix(gridView): we can use flewWrap. YEY! * feat(gridView): use Animated.Value * fix(gridView): polished code * refactor(gridViewExample): polished code * fix(gridView): unused variable
1 parent 2637403 commit 70427a8

File tree

3 files changed

+378
-262
lines changed

3 files changed

+378
-262
lines changed

example/src/GridViewExample.js

+9-12
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,15 @@
1-
/* @flow */
1+
// @flow
22

33
import React, { Component } from 'react';
44
import { View, StyleSheet } from 'react-native';
55
import { Colors, Card, Text, GridView } from 'react-native-paper';
6-
76
const CARD_SIZE = 160;
87

98
export default class GridViewExample extends Component {
109
static title = 'GridView';
1110

1211
state = {
1312
items: [],
14-
dataSource: new GridView.DataSource({
15-
rowHasChanged: (r1, r2) => r1 !== r2,
16-
}),
1713
};
1814

1915
componentWillMount() {
@@ -29,25 +25,26 @@ export default class GridViewExample extends Component {
2925
}
3026

3127
for (let i = 0; i < 100; i++) {
32-
items.push(itemsLength + i);
28+
items.push({ id: itemsLength + i });
3329
}
3430

3531
this.setState({
3632
items,
37-
dataSource: this.state.dataSource.cloneWithRows(items),
3833
});
3934
};
4035

41-
_renderRow = index => {
36+
_renderItem = item => {
4237
return (
4338
<Card style={styles.tile}>
4439
<View style={styles.inner}>
45-
<Text style={styles.text}>{index}</Text>
40+
<Text style={styles.text}>{item.id}</Text>
4641
</View>
4742
</Card>
4843
);
4944
};
5045

46+
_keyExtractor = item => item.id;
47+
5148
_getNumberOfColumns = (width: number) => {
5249
return Math.floor(width / CARD_SIZE);
5350
};
@@ -56,11 +53,11 @@ export default class GridViewExample extends Component {
5653
return (
5754
<GridView
5855
{...this.props}
59-
removeClippedSubviews={false}
6056
spacing={8}
6157
getNumberOfColumns={this._getNumberOfColumns}
62-
dataSource={this.state.dataSource}
63-
renderRow={this._renderRow}
58+
data={this.state.items}
59+
keyExtractor={this._keyExtractor}
60+
renderItem={this._renderItem}
6461
onEndReached={this._genRows}
6562
/>
6663
);

src/components/GridView.js

+58-64
Original file line numberDiff line numberDiff line change
@@ -2,145 +2,139 @@
22

33
import React, { PureComponent } from 'react';
44
import PropTypes from 'prop-types';
5-
import { Dimensions, ListView, StyleSheet, ViewPropTypes } from 'react-native';
5+
import {
6+
Animated,
7+
StyleSheet,
8+
VirtualizedList,
9+
ViewPropTypes,
10+
} from 'react-native';
611
import withTheme from '../core/withTheme';
712
import type { Theme } from '../types/Theme';
813

9-
type Layout = {
10-
width: number,
11-
};
12-
1314
type Props = {
14-
dataSource: ListView.DataSource,
15+
/**
16+
* Item's spacing
17+
*/
1518
spacing: number,
19+
/**
20+
* Function which determine number of columns.
21+
*/
1622
getNumberOfColumns: (width: number) => number,
17-
renderSectionHeader?: (...args: any) => any,
18-
renderRow: (...args: any) => any,
19-
initialLayout: Layout,
23+
/**
24+
* Data for the list
25+
*/
26+
data: Array<any>,
27+
/**
28+
* Function which should return ID base on the item.
29+
*/
30+
keyExtractor: (item: any) => string,
31+
contentContainerStyle: ?Object,
32+
/**
33+
* Component for rendering item
34+
*/
35+
renderItem: (item: any) => React$Element<*>,
2036
onLayout?: Function,
21-
contentContainerStyle?: any,
2237
theme: Theme,
2338
};
2439

2540
type DefaultProps = {
26-
initialLayout: Layout,
2741
getNumberOfColumns: (width: number) => number,
2842
spacing: number,
2943
};
3044

3145
type State = {
32-
layout: Layout,
46+
itemWidth: Animated.Value,
3347
};
3448

3549
class GridView extends PureComponent<DefaultProps, Props, State> {
3650
static propTypes = {
37-
dataSource: PropTypes.instanceOf(ListView.DataSource).isRequired,
51+
data: PropTypes.array.isRequired,
3852
spacing: PropTypes.number.isRequired,
3953
getNumberOfColumns: PropTypes.func.isRequired,
40-
renderSectionHeader: PropTypes.func,
41-
renderRow: PropTypes.func.isRequired,
54+
renderItem: PropTypes.func.isRequired,
55+
keyExtractor: PropTypes.func.isRequired,
4256
onLayout: PropTypes.func,
4357
theme: PropTypes.object.isRequired,
4458
contentContainerStyle: ViewPropTypes.style,
4559
};
4660

4761
static defaultProps = {
48-
initialLayout: { width: Dimensions.get('window').width },
4962
getNumberOfColumns: () => 1,
5063
spacing: 0,
5164
};
5265

53-
static DataSource = ListView.DataSource;
54-
5566
constructor(props: Props) {
5667
super(props);
5768

5869
this.state = {
59-
layout: props.initialLayout,
70+
itemWidth: new Animated.Value(0),
6071
};
6172
}
6273

6374
state: State;
6475

65-
scrollTo(options: any) {
66-
this._root.scrollTo(options);
67-
}
76+
_root: VirtualizedList;
6877

69-
_root: Object;
78+
scrollToIndex = (params: Object) => this._root.scrollToIndex(params);
79+
scrollToItem = (params: Object) => this._root.scrollToItem(params);
80+
scrollToEnd = (params?: Object) => this._root.scrollToEnd(params);
81+
scrollToOffset = (params: Object) => this._root.scrollToOffset(params);
7082

71-
_renderSectionHeader = (...args) => {
72-
const header = this.props.renderSectionHeader
73-
? this.props.renderSectionHeader(...args)
74-
: null;
75-
if (!header) {
76-
return header;
77-
}
78-
const { width } = this.state.layout;
79-
return React.cloneElement(header, {
80-
style: [header.props.style, { width }],
81-
});
82-
};
83+
_renderItem = ({ item }) => {
84+
const { spacing, renderItem } = this.props;
8385

84-
_renderRow = (...args: any) => {
85-
const containerWidth = this.state.layout.width;
86-
const { getNumberOfColumns, spacing } = this.props;
8786
const style = {
88-
width:
89-
(containerWidth - spacing) / getNumberOfColumns(containerWidth) -
90-
spacing,
87+
width: this.state.itemWidth,
9188
margin: spacing / 2,
9289
};
93-
const row = this.props.renderRow(...args);
94-
if (!row) {
95-
return row;
96-
}
97-
return React.cloneElement(row, {
98-
style: [row.props.style, style],
99-
});
90+
91+
return <Animated.View style={style}>{renderItem(item)}</Animated.View>;
10092
};
10193

10294
_handleLayout = (e: any) => {
95+
const { getNumberOfColumns, spacing } = this.props;
96+
10397
if (this.props.onLayout) {
10498
this.props.onLayout(e);
10599
}
106100

107-
if (this.state.layout.width === e.nativeEvent.layout.width) {
108-
return;
109-
}
101+
const layoutWidth = e.nativeEvent.layout.width;
110102

111-
const containerWidth = e.nativeEvent.layout.width;
112-
113-
this.setState({
114-
layout: { width: containerWidth },
115-
});
103+
this.state.itemWidth.setValue(
104+
(layoutWidth - spacing) / getNumberOfColumns(layoutWidth) - spacing
105+
);
116106
};
117107

118-
_setRef = (c: Object) => (this._root = c);
108+
_getItemCount = (data: Array<any>) => data.length;
109+
110+
_getItem = (data, index) => data[index];
119111

120112
render() {
121-
const { spacing, theme } = this.props;
113+
const { spacing, theme, data, keyExtractor } = this.props;
122114
return (
123-
<ListView
115+
<VirtualizedList
124116
{...this.props}
125-
key={`grid-${this.state.layout.width}`}
117+
data={data}
118+
getItemCount={this._getItemCount}
119+
getItem={this._getItem}
126120
onLayout={this._handleLayout}
127-
renderSectionHeader={this._renderSectionHeader}
128-
renderRow={this._renderRow}
121+
renderItem={this._renderItem}
122+
keyExtractor={keyExtractor}
123+
ref={c => (this._root = c)}
129124
contentContainerStyle={[
130125
styles.grid,
131126
{ padding: spacing / 2, backgroundColor: theme.colors.background },
132127
this.props.contentContainerStyle,
133128
]}
134-
ref={this._setRef}
135129
/>
136130
);
137131
}
138132
}
139133

140134
const styles = StyleSheet.create({
141135
grid: {
136+
flexWrap: 'wrap', // Warning is misleading https://github.com/facebook/react-native/issues/15772
142137
flexDirection: 'row',
143-
flexWrap: 'wrap',
144138
alignItems: 'flex-start',
145139
},
146140
});

0 commit comments

Comments
 (0)