Don't wrap ListEmptyComponent in an extra view

Summary:
A common UI pattern for list empty states is some text/images centered inside the visible part of the list. This is pretty hard to do currently because we wrap ListEmptyComponent with an extra view with no way to style it so we cannot just use `flex: 1` to make it fill the available space.

- Added an example of ListEmptyComponent in the FlatList example in RNTester

Before (no way to make ListEmptyComponent fill the space):
<img width="377" alt="screen shot 2018-03-05 at 5 24 15 pm" src="https://user-images.githubusercontent.com/2677334/37003152-129db3ac-209a-11e8-9600-110f10d57144.png">

After:
<img width="377" alt="screen shot 2018-03-05 at 5 09 20 pm" src="https://user-images.githubusercontent.com/2677334/37002809-e6971178-2098-11e8-8cf7-74bfb2f6a992.png">

- Tested some edge cases like returning null from the ListEmptyComponent

- Tested in an app that uses FlatList + ListEmptyComponent

[GENERAL] [MINOR] [VirtualizedList] - Don't wrap ListEmptyComponent in an extra view
Closes https://github.com/facebook/react-native/pull/18206

Differential Revision: D7266274

Pulled By: sahrens

fbshipit-source-id: 4636d2418474a4c86ac63e5e18a9afc391a518c5
This commit is contained in:
Janic Duplessis 2018-03-13 17:57:36 -07:00 committed by Facebook Github Bot
parent 9737774b96
commit db061ea8c7
4 changed files with 38 additions and 20 deletions

View File

@ -850,23 +850,25 @@ class VirtualizedList extends React.PureComponent<Props, State> {
);
}
} else if (ListEmptyComponent) {
const element = React.isValidElement(ListEmptyComponent) ? (
const element: React.Element<any> = (React.isValidElement(
ListEmptyComponent,
) ? (
ListEmptyComponent
) : (
// $FlowFixMe
<ListEmptyComponent />
);
): any);
cells.push(
<View
key="$empty"
onLayout={this._onLayoutEmpty}
style={inversionStyle}>
{/*
Flow doesn't know this is a React.Element and not a React.Component
$FlowFixMe https://fburl.com/b9xmtm09
*/}
{element}
</View>,
React.cloneElement(element, {
key: '$empty',
onLayout: event => {
this._onLayoutEmpty(event);
if (element.props.onLayout) {
element.props.onLayout(event);
}
},
style: [element.props.style, inversionStyle],
}),
);
}
if (ListFooterComponent) {

View File

@ -683,12 +683,7 @@ exports[`VirtualizedList renders empty list with empty component 1`] = `
>
<header />
</View>
<View
onLayout={[Function]}
style={null}
>
<empty />
</View>
<empty />
<View
onLayout={[Function]}
style={null}

View File

@ -27,6 +27,7 @@ const {
FooterComponent,
HeaderComponent,
ItemComponent,
ListEmptyComponent,
ItemSeparatorComponent,
PlainInput,
SeparatorComponent,
@ -58,6 +59,7 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> {
fixedHeight: true,
logViewable: false,
virtualized: true,
empty: false,
};
_onChangeFilterText = (filterText) => {
@ -109,8 +111,9 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> {
{renderSmallSwitchOption(this, 'virtualized')}
{renderSmallSwitchOption(this, 'horizontal')}
{renderSmallSwitchOption(this, 'fixedHeight')}
{renderSmallSwitchOption(this, 'logViewable')}
{renderSmallSwitchOption(this, 'log')}
{renderSmallSwitchOption(this, 'inverted')}
{renderSmallSwitchOption(this, 'empty')}
{renderSmallSwitchOption(this, 'debug')}
<Spindicator value={this._scrollPos} />
</View>
@ -120,7 +123,8 @@ class FlatListExample extends React.PureComponent<{}, $FlowFixMeState> {
ItemSeparatorComponent={ItemSeparatorComponent}
ListHeaderComponent={<HeaderComponent />}
ListFooterComponent={FooterComponent}
data={filteredData}
ListEmptyComponent={ListEmptyComponent}
data={this.state.empty ? [] : filteredData}
debug={this.state.debug}
disableVirtualization={!this.state.virtualized}
getItemLayout={this.state.fixedHeight ?
@ -210,6 +214,7 @@ const styles = StyleSheet.create({
},
list: {
backgroundColor: 'white',
flexGrow: 1,
},
options: {
flexDirection: 'row',

View File

@ -117,6 +117,16 @@ class HeaderComponent extends React.PureComponent<{}> {
}
}
class ListEmptyComponent extends React.PureComponent<{}> {
render() {
return (
<View style={styles.listEmpty}>
<Text>The list is empty :o</Text>
</View>
);
}
}
class SeparatorComponent extends React.PureComponent<{}> {
render() {
return <View style={styles.separator} />;
@ -240,6 +250,11 @@ const styles = StyleSheet.create({
headerFooterContainer: {
backgroundColor: 'rgb(239, 239, 244)',
},
listEmpty: {
alignItems: 'center',
justifyContent: 'center',
flexGrow: 1,
},
horizItem: {
alignSelf: 'flex-start', // Necessary for touch highlight
},
@ -317,6 +332,7 @@ const styles = StyleSheet.create({
module.exports = {
FooterComponent,
HeaderComponent,
ListEmptyComponent,
ItemComponent,
ItemSeparatorComponent,
PlainInput,