Summary:
Changelog:
[General][Changed] - Refactor SectionListExample in RNTester to functional component

Reviewed By: nadiia

Differential Revision: D25986316

fbshipit-source-id: b448316d1563cb1433cc36fe150ad8b4c0e45f54
This commit is contained in:
Luna Wei 2021-01-22 13:44:19 -08:00 коммит произвёл Facebook GitHub Bot
Родитель d0a455b3c3
Коммит 1a8d641469
1 изменённых файлов: 202 добавлений и 201 удалений

Просмотреть файл

@ -33,16 +33,50 @@ const {
StyleSheet,
Text,
View,
SectionList,
} = require('react-native');
import type {Item} from '../../components/ListExampleShared';
const VIEWABILITY_CONFIG = {
minimumViewTime: 3000,
viewAreaCoveragePercentThreshold: 100,
waitForInteraction: true,
};
const CONSTANT_SECTION_EXAMPLES = [
{
key: 'empty section',
data: [],
},
{
renderItem: renderStackedItem,
key: 's1',
data: [
{
title: 'Item In Header Section',
text: 'Section s1',
key: 'header item',
},
],
},
{
key: 's2',
data: [
{
noImage: true,
title: '1st item',
text: 'Section s2',
key: 'noimage0',
},
{
noImage: true,
title: '2nd item',
text: 'Section s2',
key: 'noimage1',
},
],
},
];
const renderSectionHeader = ({section}) => (
<View style={styles.header}>
<Text style={styles.headerText}>SECTION HEADER: {section.key}</Text>
@ -73,195 +107,92 @@ const EmptySectionList = () => (
</View>
);
class SectionListExample extends React.PureComponent<{...}, $FlowFixMeState> {
state:
| any
| {|
data: Array<Item>,
debug: boolean,
filterText: string,
inverted: boolean,
logViewable: boolean,
virtualized: boolean,
|} = {
data: genItemData(1000),
debug: false,
filterText: '',
logViewable: false,
virtualized: true,
inverted: false,
const renderItemComponent = setItemState => ({item, separators}) => {
if (isNaN(item.key)) {
return;
}
const onPress = () => {
const updatedItem = pressItem(item);
setItemState(updatedItem);
};
_scrollPos = new Animated.Value(0);
_scrollSinkY = Animated.event(
[{nativeEvent: {contentOffset: {y: this._scrollPos}}}],
{useNativeDriver: true},
);
_sectionListRef: ?React.ElementRef<typeof Animated.SectionList> = null;
_captureRef = ref => {
this._sectionListRef = ref;
};
_scrollToLocation(sectionIndex: number, itemIndex: number) {
this._sectionListRef &&
this._sectionListRef.scrollToLocation({sectionIndex, itemIndex});
}
_setBooleanValue: string => boolean => void = key => value =>
this.setState({[key]: value});
render(): React.Node {
const filterRegex = new RegExp(String(this.state.filterText), 'i');
const filter = item =>
filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = this.state.data.filter(filter);
const filteredSectionData = [];
let startIndex = 0;
const endIndex = filteredData.length - 1;
for (let ii = 10; ii <= endIndex + 10; ii += 10) {
filteredSectionData.push({
key: `${filteredData[startIndex].key} - ${
filteredData[Math.min(ii - 1, endIndex)].key
}`,
data: filteredData.slice(startIndex, ii),
});
startIndex = ii;
}
return (
<RNTesterPage noSpacer={true} noScroll={true}>
<View style={styles.searchRow}>
<PlainInput
onChangeText={filterText => {
this.setState(() => ({filterText}));
}}
placeholder="Search..."
value={this.state.filterText}
/>
<View style={styles.optionSection}>
{renderSmallSwitchOption(
'virtualized',
this.state.virtualized,
this._setBooleanValue('virtualized'),
)}
{renderSmallSwitchOption(
'Log Viewable',
this.state.logViewable,
this._setBooleanValue('logViewable'),
)}
{renderSmallSwitchOption(
'Debug',
this.state.debug,
this._setBooleanValue('debug'),
)}
{renderSmallSwitchOption(
'Inverted',
this.state.inverted,
this._setBooleanValue('inverted'),
)}
<Spindicator value={this._scrollPos} />
</View>
<View style={styles.scrollToRow}>
<Text>scroll to:</Text>
<Button
title="Item A"
onPress={() => this._scrollToLocation(2, 1)}
/>
<Button
title="Item B"
onPress={() => this._scrollToLocation(3, 6)}
/>
<Button
title="Item C"
onPress={() => this._scrollToLocation(6, 3)}
/>
</View>
</View>
<SeparatorComponent />
<Animated.SectionList
ref={this._captureRef}
ListHeaderComponent={HeaderComponent}
ListFooterComponent={FooterComponent}
SectionSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="SECTION SEPARATOR" />
)}
ItemSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="ITEM SEPARATOR" />
)}
debug={this.state.debug}
inverted={this.state.inverted}
disableVirtualization={!this.state.virtualized}
onRefresh={() => Alert.alert('onRefresh: nothing to refresh :P')}
onScroll={this._scrollSinkY}
onViewableItemsChanged={this._onViewableItemsChanged}
refreshing={false}
renderItem={this._renderItemComponent}
renderSectionHeader={renderSectionHeader}
renderSectionFooter={renderSectionFooter}
stickySectionHeadersEnabled
initialNumToRender={10}
ListEmptyComponent={EmptySectionList}
onEndReached={() =>
Alert.alert(
'onEndReached called',
'You have reached the end of this list',
)
}
onEndReachedThreshold={0}
sections={[
{
key: 'empty section',
data: [],
},
{
renderItem: renderStackedItem,
key: 's1',
data: [
{
title: 'Item In Header Section',
text: 'Section s1',
key: 'header item',
},
],
},
{
key: 's2',
data: [
{
noImage: true,
title: '1st item',
text: 'Section s2',
key: 'noimage0',
},
{
noImage: true,
title: '2nd item',
text: 'Section s2',
key: 'noimage1',
},
],
},
...filteredSectionData,
]}
style={styles.list}
viewabilityConfig={VIEWABILITY_CONFIG}
/>
</RNTesterPage>
);
}
_renderItemComponent = ({item, separators}) => (
return (
<ItemComponent
item={item}
onPress={this._pressItem}
onPress={onPress}
onHideUnderlay={separators.unhighlight}
onShowUnderlay={separators.highlight}
/>
);
};
// This is called when items change viewability by scrolling into our out of
// the viewable area.
_onViewableItemsChanged = (info: {
const onScrollToIndexFailed = (info: {
index: number,
c: number,
averageItemLength: number,
}) => {
console.warn('onScrollToIndexFailed. See comment in callback', info);
/**
* scrollToLocation() can only scroll to viewable area.
* For any failure cases this callback will get triggered with `info` object
*
* The idea is to calculate a yPosition from `info` to call scrollResponder.scrollTo on.
*
* const scrollResponder = ref.current?.getScrollResponder();
* const positionY = some value we calculate from `info`;
* if (scrollResponder != null) {
* scrollResponder.scrollTo({x, y:positionY, animated: true});
* }
*/
};
function SectionListExample(Props: {...}): React.Element<typeof RNTesterPage> {
const scrollPos = new Animated.Value(0);
const scrollSinkY = Animated.event(
[{nativeEvent: {contentOffset: {y: scrollPos}}}],
{useNativeDriver: true},
);
const [filterText, setFilterText] = React.useState('');
const [virtualized, setVirtualized] = React.useState(true);
const [logViewable, setLogViewable] = React.useState(false);
const [debug, setDebug] = React.useState(false);
const [inverted, setInverted] = React.useState(false);
const [data, setData] = React.useState(genItemData(1000));
const filterRegex = new RegExp(String(filterText), 'i');
const filter = item =>
filterRegex.test(item.text) || filterRegex.test(item.title);
const filteredData = data.filter(filter);
const filteredSectionData = [...CONSTANT_SECTION_EXAMPLES];
let startIndex = 0;
const endIndex = filteredData.length - 1;
for (let ii = 10; ii <= endIndex + 10; ii += 10) {
filteredSectionData.push({
key: `${filteredData[startIndex].key} - ${
filteredData[Math.min(ii - 1, endIndex)].key
}`,
data: filteredData.slice(startIndex, ii),
});
startIndex = ii;
}
const setItemPress = item => {
if (isNaN(item.key)) {
return;
}
const index = Number(item.key);
setData([...data.slice(0, index), item, ...data.slice(index + 1)]);
};
const ref = React.useRef<?React.ElementRef<typeof SectionList>>(null);
const scrollToLocation = (sectionIndex, itemIndex) => {
if (ref != null && ref.current?.scrollToLocation != null) {
ref.current.scrollToLocation({sectionIndex, itemIndex});
}
};
const onViewableItemsChanged = (info: {
changed: Array<{
key: string,
isViewable: boolean,
@ -273,7 +204,7 @@ class SectionListExample extends React.PureComponent<{...}, $FlowFixMeState> {
...
}) => {
// Impressions can be logged here
if (this.state.logViewable) {
if (logViewable) {
infoLog(
'onViewableItemsChanged: ',
info.changed.map((v: Object) => ({
@ -285,24 +216,95 @@ class SectionListExample extends React.PureComponent<{...}, $FlowFixMeState> {
}
};
_pressItem = (key: string) => {
if (isNaN(key)) {
return;
}
const index = Number(key);
const itemState = pressItem(this.state.data[index]);
this.setState(state => ({
...state,
data: [
...state.data.slice(0, index),
itemState,
...state.data.slice(index + 1),
],
}));
};
return (
<RNTesterPage noSpacer={true} noScroll={true}>
<View style={styles.searchRow}>
<PlainInput
onChangeText={text => setFilterText(text)}
placeholder="Search..."
value={filterText}
/>
<View style={styles.optionSection}>
{renderSmallSwitchOption('Virtualized', virtualized, setVirtualized)}
{renderSmallSwitchOption('Log Viewable', logViewable, setLogViewable)}
{renderSmallSwitchOption('Debug', debug, setDebug)}
{renderSmallSwitchOption('Inverted', inverted, setInverted)}
<Spindicator value={scrollPos} />
</View>
<View style={styles.scrollToColumn}>
<Text>scroll to:</Text>
<View style={styles.button}>
<Button
title="Top"
onPress={() => scrollToLocation(Math.max(0, 2), 0)}
/>
</View>
<View style={styles.button}>
<Button
title="3rd Section"
onPress={() => scrollToLocation(Math.max(0, 3), 0)}
/>
</View>
<View style={styles.button}>
<Button
title="6th Section"
onPress={() => scrollToLocation(Math.max(0, 6), 0)}
/>
</View>
<View style={styles.button}>
<Button
title="Out of Viewable Area (See warning) "
onPress={() =>
scrollToLocation(filteredSectionData.length - 1, 0)
}
/>
</View>
</View>
</View>
<SeparatorComponent />
<Animated.SectionList
ref={ref}
ListHeaderComponent={HeaderComponent}
ListFooterComponent={FooterComponent}
SectionSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="SECTION SEPARATOR" />
)}
ItemSeparatorComponent={info => (
<CustomSeparatorComponent {...info} text="ITEM SEPARATOR" />
)}
debug={debug}
inverted={inverted}
disableVirtualization={!virtualized}
onRefresh={() => Alert.alert('onRefresh: nothing to refresh :P')}
onScroll={scrollSinkY}
onViewableItemsChanged={onViewableItemsChanged}
onScrollToIndexFailed={onScrollToIndexFailed}
refreshing={false}
renderItem={renderItemComponent(setItemPress)}
renderSectionHeader={renderSectionHeader}
renderSectionFooter={renderSectionFooter}
stickySectionHeadersEnabled
initialNumToRender={10}
ListEmptyComponent={EmptySectionList}
onEndReached={() =>
Alert.alert(
'onEndReached called',
'You have reached the end of this list',
)
}
onEndReachedThreshold={0}
sections={filteredSectionData}
style={styles.list}
viewabilityConfig={VIEWABILITY_CONFIG}
/>
</RNTesterPage>
);
}
const styles = StyleSheet.create({
button: {
marginTop: 5,
},
customSeparator: {
backgroundColor: 'rgb(200, 199, 204)',
},
@ -324,9 +326,8 @@ const styles = StyleSheet.create({
searchRow: {
paddingHorizontal: 10,
},
scrollToRow: {
flexDirection: 'row',
alignItems: 'center',
scrollToColumn: {
flexDirection: 'column',
paddingHorizontal: 8,
},
separatorText: {