Skip to content

Commit ede7d05

Browse files
author
Mathis Neumann
committed
enhance example to include a window scroller
1 parent c1d7cbd commit ede7d05

File tree

4 files changed

+267
-5
lines changed

4 files changed

+267
-5
lines changed

packages/examples/src/RNTester/FlatListExample.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -145,6 +145,7 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> {
145145
renderItem={this._renderItemComponent}
146146
contentContainerStyle={styles.list}
147147
viewabilityConfig={VIEWABILITY_CONFIG}
148+
// useWindowScrolling
148149
/>
149150
</View>
150151
</RNTesterPage>
Lines changed: 241 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,241 @@
1+
/**
2+
* Copyright (c) 2015-present, Facebook, Inc.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*
7+
* @flow
8+
* @providesModule FlatListWindowScrollExample
9+
*/
10+
'use strict';
11+
12+
const Alert = require('Alert');
13+
const React = require('react');
14+
const ReactNative = require('react-native');
15+
const {
16+
Animated,
17+
FlatList,
18+
StyleSheet,
19+
View,
20+
} = ReactNative;
21+
22+
const RNTesterPage = require('./RNTesterPage');
23+
24+
const infoLog = require('infoLog');
25+
26+
const {
27+
FooterComponent,
28+
HeaderComponent,
29+
ItemComponent,
30+
ItemSeparatorComponent,
31+
PlainInput,
32+
SeparatorComponent,
33+
Spindicator,
34+
genItemData,
35+
getItemLayout,
36+
pressItem,
37+
renderSmallSwitchOption,
38+
} = require('./ListExampleShared');
39+
40+
const AnimatedFlatList = Animated.createAnimatedComponent(FlatList);
41+
42+
const VIEWABILITY_CONFIG = {
43+
minimumViewTime: 3000,
44+
viewAreaCoveragePercentThreshold: 100,
45+
waitForInteraction: true,
46+
};
47+
48+
class FlatListWindowScrollExample extends React.PureComponent<{}, $FlowFixMeState> {
49+
static title = '<FlatList Window Scroll>';
50+
static description = 'Performant, scrollable list of data based on the browsers scroll position.';
51+
52+
state = {
53+
data: genItemData(100),
54+
debug: false,
55+
horizontal: false,
56+
inverted: false,
57+
filterText: '',
58+
fixedHeight: true,
59+
logViewable: false,
60+
virtualized: true,
61+
};
62+
63+
_onChangeFilterText = (filterText) => {
64+
this.setState({filterText});
65+
};
66+
67+
_onChangeScrollToIndex = (text) => {
68+
this._listRef.getNode().scrollToIndex({viewPosition: 0.5, index: Number(text)});
69+
};
70+
71+
_scrollPos = new Animated.Value(0);
72+
_scrollSinkX = Animated.event(
73+
[{nativeEvent: { contentOffset: { x: this._scrollPos } }}],
74+
{useNativeDriver: true},
75+
);
76+
_scrollSinkY = Animated.event(
77+
[{nativeEvent: { contentOffset: { y: this._scrollPos } }}],
78+
{useNativeDriver: true},
79+
);
80+
81+
componentDidUpdate() {
82+
this._listRef.getNode().recordInteraction(); // e.g. flipping logViewable switch
83+
}
84+
85+
componentDidMount() {
86+
document.body.className = document.body.className.replace('blockBodyScroll', '')
87+
}
88+
componentWillUnmount() {
89+
document.body.className = document.body.className + ' blockBodyScroll'
90+
}
91+
92+
render() {
93+
const filterRegex = new RegExp(String(this.state.filterText), 'i');
94+
const filter = (item) => (
95+
filterRegex.test(item.text) || filterRegex.test(item.title)
96+
);
97+
const filteredData = this.state.data.filter(filter);
98+
return (
99+
<RNTesterPage
100+
noSpacer={true}
101+
noScroll={true}>
102+
<View style={styles.container}>
103+
<View style={styles.searchRow}>
104+
<View style={styles.options}>
105+
<PlainInput
106+
onChangeText={this._onChangeFilterText}
107+
placeholder="Search..."
108+
value={this.state.filterText}
109+
/>
110+
<PlainInput
111+
onChangeText={this._onChangeScrollToIndex}
112+
placeholder="scrollToIndex..."
113+
/>
114+
</View>
115+
<View style={styles.options}>
116+
{renderSmallSwitchOption(this, 'virtualized')}
117+
{renderSmallSwitchOption(this, 'horizontal')}
118+
{renderSmallSwitchOption(this, 'fixedHeight')}
119+
{renderSmallSwitchOption(this, 'logViewable')}
120+
{renderSmallSwitchOption(this, 'inverted')}
121+
{renderSmallSwitchOption(this, 'debug')}
122+
</View>
123+
</View>
124+
<SeparatorComponent />
125+
<AnimatedFlatList
126+
ItemSeparatorComponent={ItemSeparatorComponent}
127+
ListHeaderComponent={<HeaderComponent />}
128+
ListFooterComponent={FooterComponent}
129+
data={filteredData}
130+
debug={this.state.debug}
131+
disableVirtualization={!this.state.virtualized}
132+
getItemLayout={this.state.fixedHeight ?
133+
this._getItemLayout :
134+
undefined
135+
}
136+
horizontal={this.state.horizontal}
137+
inverted={this.state.inverted}
138+
key={(this.state.horizontal ? 'h' : 'v') +
139+
(this.state.fixedHeight ? 'f' : 'd')
140+
}
141+
keyboardShouldPersistTaps="always"
142+
keyboardDismissMode="on-drag"
143+
legacyImplementation={false}
144+
numColumns={1}
145+
onEndReached={this._onEndReached}
146+
onRefresh={this._onRefresh}
147+
onScroll={this.state.horizontal ? this._scrollSinkX : this._scrollSinkY}
148+
onViewableItemsChanged={this._onViewableItemsChanged}
149+
ref={this._captureRef}
150+
refreshing={false}
151+
renderItem={this._renderItemComponent}
152+
contentContainerStyle={styles.list}
153+
viewabilityConfig={VIEWABILITY_CONFIG}
154+
useWindowScrolling
155+
/>
156+
157+
<View style={styles.spinIndicator}>
158+
<Spindicator value={this._scrollPos} />
159+
</View>
160+
</View>
161+
</RNTesterPage>
162+
);
163+
}
164+
_captureRef = (ref) => { this._listRef = ref; };
165+
_getItemLayout = (data: any, index: number) => {
166+
return getItemLayout(data, index, this.state.horizontal);
167+
};
168+
_onEndReached = () => {
169+
if (this.state.data.length >= 1000) {
170+
return;
171+
}
172+
this.setState((state) => ({
173+
data: state.data.concat(genItemData(100, state.data.length)),
174+
}));
175+
};
176+
_onRefresh = () => Alert.alert('onRefresh: nothing to refresh :P');
177+
_renderItemComponent = ({item, separators}) => {
178+
return (
179+
<ItemComponent
180+
item={item}
181+
horizontal={this.state.horizontal}
182+
fixedHeight={this.state.fixedHeight}
183+
onPress={this._pressItem}
184+
onShowUnderlay={separators.highlight}
185+
onHideUnderlay={separators.unhighlight}
186+
/>
187+
);
188+
};
189+
// This is called when items change viewability by scrolling into or out of
190+
// the viewable area.
191+
_onViewableItemsChanged = (info: {
192+
changed: Array<{
193+
key: string,
194+
isViewable: boolean,
195+
item: any,
196+
index: ?number,
197+
section?: any,
198+
}>
199+
}
200+
) => {
201+
// Impressions can be logged here
202+
if (this.state.logViewable) {
203+
infoLog(
204+
'onViewableItemsChanged: ',
205+
info.changed.map((v) => ({...v, item: '...'})),
206+
);
207+
}
208+
};
209+
_pressItem = (key: string) => {
210+
this._listRef.getNode().recordInteraction();
211+
pressItem(this, key);
212+
};
213+
_listRef: AnimatedFlatList;
214+
}
215+
216+
217+
const styles = StyleSheet.create({
218+
container: {
219+
backgroundColor: 'rgb(239, 239, 244)',
220+
flex: 1,
221+
},
222+
list: {
223+
backgroundColor: 'white',
224+
},
225+
options: {
226+
flexDirection: 'row',
227+
flexWrap: 'wrap',
228+
alignItems: 'center',
229+
},
230+
searchRow: {
231+
paddingHorizontal: 10,
232+
},
233+
spinIndicator: {
234+
position: 'fixed',
235+
right: 25,
236+
top: 25,
237+
zIndex: 100,
238+
}
239+
});
240+
241+
module.exports = FlatListWindowScrollExample;

packages/examples/src/RNTester/RNTesterList.web.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,10 @@ const ComponentExamples: Array<RNTesterExample> = [
2828
key: 'FlatListExample',
2929
module: require('./FlatListExample')
3030
},
31+
{
32+
key: 'FlatListWindowScrollExample',
33+
module: require('./FlatListWindowScrollExample')
34+
},
3135
{
3236
key: 'MultiColumnExample',
3337
module: require('./MultiColumnExample')

packages/examples/src/index.html

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,28 @@
55
<title>React Native examples</title>
66
<meta name="viewport" content="initial-scale=1.0,width=device-width">
77
<style>
8-
html, body { height: 100%; width: 100%; overflow: hidden; }
9-
.root { height: 100%; overflow: hidden; }
8+
9+
html, body {
10+
min-height: 100%;
11+
min-width: 100%;
12+
}
13+
#root {
14+
display: flex;
15+
}
16+
.blockBodyScroll {
17+
height: 100%;
18+
width: 100%;
19+
overflow: hidden;
20+
}
21+
.blockBodyScroll > #root {
22+
height: 100%;
23+
width: 100%;
24+
overflow: hidden;
25+
}
1026
</style>
1127
</head>
12-
<body style="height:100%">
13-
<div id="root" style="display:flex;height:100%"></div>
28+
<body class="blockBodyScroll">
29+
<div id="root"></div>
1430
<script src="./bundle.js"></script>
1531
</body>
16-
</html>
32+
</html>

0 commit comments

Comments
 (0)