Permalink
Browse files

Add SectionSeparatorComponent support

Summary: Makes it easy to render separators between sections, as opposed to between items.

Reviewed By: yungsters

Differential Revision: D4555707

fbshipit-source-id: 34572ab4b2c5b47db640543149fe2551c34ccf7b
  • Loading branch information...
sahrens authored and facebook-github-bot committed Feb 14, 2017
1 parent d8f48d1 commit a141e63ee44dea0cf782e4a383410af3e50b19a2
@@ -46,11 +46,20 @@ const {
renderSmallSwitchOption,
} = require('./ListExampleShared');
const SectionHeaderComponent = ({section}) =>
const SectionHeaderComponent = ({section}) => (
<View>
<Text style={styles.headerText}>SECTION HEADER: {section.key}</Text>
<SeparatorComponent />
</View>;
</View>
);
const SectionSeparatorComponent = () => (
<View>
<SeparatorComponent />
<Text style={styles.sectionSeparatorText}>SECTION SEPARATOR</Text>
<SeparatorComponent />
</View>
);
class SectionListExample extends React.PureComponent {
static title = '<SectionList>';
@@ -88,12 +97,12 @@ class SectionListExample extends React.PureComponent {
FooterComponent={FooterComponent}
ItemComponent={this._renderItemComponent}
SectionHeaderComponent={SectionHeaderComponent}
SectionSeparatorComponent={SectionSeparatorComponent}
SeparatorComponent={SeparatorComponent}
enableVirtualization={this.state.virtualized}
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
onViewableItemsChanged={this._onViewableItemsChanged}
refreshing={false}
shouldItemUpdate={(prev, next) => prev.item !== next.item}
sections={[
{ItemComponent: StackedItemComponent, key: 's1', data: [
{title: 'Item In Header Section', text: 's1', key: '0'}
@@ -134,6 +143,12 @@ const styles = StyleSheet.create({
searchRow: {
paddingHorizontal: 10,
},
sectionSeparatorText: {
color: 'gray',
alignSelf: 'center',
padding: 4,
fontWeight: 'bold',
},
});
module.exports = SectionListExample;
@@ -43,7 +43,7 @@ type SectionItem = any;
type SectionBase = {
// Must be provided directly on each section.
data: ?Array<SectionItem>,
data: Array<SectionItem>,
key: string,
// Optional props will override list-wide props just for this section.
@@ -78,6 +78,11 @@ type OptionalProps<SectionT: SectionBase> = {
* Rendered at the top of each section. In the future, a sticky option will be added.
*/
SectionHeaderComponent?: ?ReactClass<{section: SectionT}>,
/**
* Rendered at the bottom of every Section, except the very last one, in place of the normal
* SeparatorComponent.
*/
SectionSeparatorComponent?: ?ReactClass<*>,
/**
* Rendered at the bottom of every Item except the very last one in the last section.
*/
@@ -104,6 +109,13 @@ type OptionalProps<SectionT: SectionBase> = {
* Set this true while waiting for new data from a refresh.
*/
refreshing?: boolean,
/**
* This is an optional optimization to minimize re-rendering items.
*/
shouldItemUpdate?: (
prevProps: {item: Item, index: number},
nextProps: {item: Item, index: number}
) => boolean,
};
type Props<SectionT> = RequiredProps<SectionT> & OptionalProps<SectionT>;
@@ -46,12 +46,8 @@ type SectionItem = any;
type Section = {
// Must be provided directly on each section.
data: ?Array<SectionItem>,
data: Array<SectionItem>,
key: string,
/**
* Stick whatever you want here, e.g. meta data for rendering section headers.
*/
extra?: any,
// Optional props will override list-wide props just for this section.
ItemComponent?: ?ReactClass<{item: SectionItem, index: number}>,
@@ -74,6 +70,7 @@ type RequiredProps = {
type OptionalProps = {
ItemComponent?: ?ReactClass<{item: Item, index: number}>,
SectionHeaderComponent?: ?ReactClass<{section: Section}>,
SectionSeparatorComponent?: ?ReactClass<*>,
SeparatorComponent?: ?ReactClass<*>,
/**
* Warning: Virtualization can drastically improve memory consumption for long lists, but trashes
@@ -109,12 +106,11 @@ class VirtualizedSectionList extends React.PureComponent {
};
_keyExtractor = (item: Item, index: number) => {
const info = this._subExtractor(item, index);
const info = this._subExtractor(index);
return info && info.key;
};
_subExtractor(
item,
index: number,
): ?{
section: Section,
@@ -144,7 +140,7 @@ class VirtualizedSectionList extends React.PureComponent {
_convertViewable = (viewable: Viewable): ?Viewable => {
invariant(viewable.index != null, 'Received a broken Viewable');
const info = this._subExtractor(viewable.item, viewable.index);
const info = this._subExtractor(viewable.index);
if (!info) {
return null;
}
@@ -169,31 +165,64 @@ class VirtualizedSectionList extends React.PureComponent {
}
_isItemSticky = (item, index) => {
const info = this._subExtractor(item, index);
const info = this._subExtractor(index);
return info && info.index == null;
};
_renderItem = ({item, index}: {item: Item, index: number}) => {
const info = this._subExtractor(item, index);
const info = this._subExtractor(index);
if (!info) {
return null;
} else if (info.index == null) {
return <this.props.SectionHeaderComponent section={info.section} />;
} else {
const ItemComponent = info.section.ItemComponent || this.props.ItemComponent;
const SeparatorComponent = info.section.SeparatorComponent || this.props.SeparatorComponent;
const {SectionSeparatorComponent} = this.props;
const [shouldRenderSectionSeparator, shouldRenderItemSeparator] =
this._shouldRenderSeparators(index, info);
return (
<View>
<ItemComponent item={item} index={info.index} />
{SeparatorComponent && index < this.state.childProps.getItemCount() - 1
? <SeparatorComponent />
: null
}
{shouldRenderItemSeparator ? <SeparatorComponent /> : null}
{shouldRenderSectionSeparator ? <SectionSeparatorComponent /> : null}
</View>
);
}
};
_shouldRenderSeparators(index: number, info?: ?Object): [boolean, boolean] {
info = info || this._subExtractor(index);
if (!info) {
return [false, false];
}
const SeparatorComponent = info.section.SeparatorComponent || this.props.SeparatorComponent;
const {SectionSeparatorComponent} = this.props;
const lastItemIndex = this.state.childProps.getItemCount() - 1;
let shouldRenderSectionSeparator = false;
if (SectionSeparatorComponent) {
shouldRenderSectionSeparator =
info.index === info.section.data.length - 1 && index < lastItemIndex;
}
const shouldRenderItemSeparator = SeparatorComponent && !shouldRenderSectionSeparator &&
index < lastItemIndex;
return [shouldRenderSectionSeparator, shouldRenderItemSeparator];
}
_shouldItemUpdate = (prev, next) => {
const {shouldItemUpdate} = this.props;
if (!shouldItemUpdate || shouldItemUpdate(prev, next)) {
return true;
}
if (prev.index !== next.index) {
const [secSepPrev, itSepPrev] = this._shouldRenderSeparators(prev.index);
const [secSepNext, itSepNext] = this._shouldRenderSeparators(next.index);
if (secSepPrev !== secSepNext || itSepPrev !== itSepNext) {
return true;
}
}
}
_computeState(props: Props) {
const itemCount = props.sections.reduce((v, section) => v + section.data.length + 1, 0);
return {
@@ -208,6 +237,7 @@ class VirtualizedSectionList extends React.PureComponent {
keyExtractor: this._keyExtractor,
onViewableItemsChanged:
props.onViewableItemsChanged ? this._onViewableItemsChanged : undefined,
shouldItemUpdate: this._shouldItemUpdate,
},
};
}

0 comments on commit a141e63

Please sign in to comment.