Skip to content

Commit

Permalink
[change] Update VirtualizedSectionList implementation
Browse files Browse the repository at this point in the history
Mirror contents of React Native 0.57.5

Ref #1172
  • Loading branch information
necolas committed Nov 17, 2018
1 parent 5eeba38 commit d513c4b
Showing 1 changed file with 86 additions and 70 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,13 @@
* @noflow
* @format
*/
'use strict';

import React from 'react';
import View from '../../../exports/View';
import VirtualizedList, { type Props as VirtualizedListProps } from '../VirtualizedList';
import invariant from 'fbjs/lib/invariant';

import type { ViewToken } from '../ViewabilityHelper';

type Item = any;
Expand All @@ -33,12 +35,12 @@ type SectionBase = {
updateProps: (select: 'leading' | 'trailing', newProps: Object) => void,
},
}) => ?React.Element<any>,
ItemSeparatorComponent?: ?React.ComponentType<*>,
ItemSeparatorComponent?: ?React.ComponentType<any>,
keyExtractor?: (item: SectionItem, index: ?number) => string,

// TODO: support more optional/override props
// FooterComponent?: ?ReactClass<*>,
// HeaderComponent?: ?ReactClass<*>,
// FooterComponent?: ?ReactClass<any>,
// HeaderComponent?: ?ReactClass<any>,
// onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void,
};

Expand All @@ -50,11 +52,11 @@ type OptionalProps<SectionT: SectionBase> = {
/**
* Rendered after the last item in the last section.
*/
ListFooterComponent?: ?(React.ComponentType<*> | React.Element<any>),
ListFooterComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Rendered at the very beginning of the list.
*/
ListHeaderComponent?: ?(React.ComponentType<*> | React.Element<any>),
ListHeaderComponent?: ?(React.ComponentType<any> | React.Element<any>),
/**
* Default renderer for every item in every section.
*/
Expand All @@ -80,11 +82,11 @@ type OptionalProps<SectionT: SectionBase> = {
* Rendered at the bottom of every Section, except the very last one, in place of the normal
* ItemSeparatorComponent.
*/
SectionSeparatorComponent?: ?React.ComponentType<*>,
SectionSeparatorComponent?: ?React.ComponentType<any>,
/**
* Rendered at the bottom of every Item except the very last one in the last section.
*/
ItemSeparatorComponent?: ?React.ComponentType<*>,
ItemSeparatorComponent?: ?React.ComponentType<any>,
/**
* Warning: Virtualization can drastically improve memory consumption for long lists, but trashes
* the state of items when they scroll out of the render window, so make sure all relavent data is
Expand All @@ -97,7 +99,7 @@ type OptionalProps<SectionT: SectionBase> = {
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
* sure to also set the `refreshing` prop correctly.
*/
onRefresh?: ?Function,
onRefresh?: ?() => void,
/**
* Called when the viewability of rows changes, as defined by the
* `viewabilityConfig` prop.
Expand Down Expand Up @@ -130,10 +132,6 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
Props<SectionT>,
State,
> {
props: Props<SectionT>;

state: State;

static defaultProps: DefaultProps = {
...VirtualizedList.defaultProps,
data: [],
Expand All @@ -160,6 +158,48 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
return this._listRef;
}

constructor(props: Props<SectionT>, context: Object) {
super(props, context);
this.state = this._computeState(props);
}

UNSAFE_componentWillReceiveProps(nextProps: Props<SectionT>) {
this.setState(this._computeState(nextProps));
}

_computeState(props: Props<SectionT>): State {
const offset = props.ListHeaderComponent ? 1 : 0;
const stickyHeaderIndices = [];
const itemCount = props.sections.reduce((v, section) => {
stickyHeaderIndices.push(v + offset);
return v + section.data.length + 2; // Add two for the section header and footer.
}, 0);

return {
childProps: {
...props,
renderItem: this._renderItem,
ItemSeparatorComponent: undefined, // Rendered with renderItem
data: props.sections,
getItemCount: () => itemCount,
getItem,
keyExtractor: this._keyExtractor,
onViewableItemsChanged: props.onViewableItemsChanged
? this._onViewableItemsChanged
: undefined,
stickyHeaderIndices: props.stickySectionHeadersEnabled
? stickyHeaderIndices
: undefined,
},
};
}

render() {
return (
<VirtualizedList {...this.state.childProps} ref={this._captureRef} />
);
}

_keyExtractor = (item: Item, index: number) => {
const info = this._subExtractor(index);
return (info && info.key) || String(index);
Expand Down Expand Up @@ -303,7 +343,7 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
_getSeparatorComponent(
index: number,
info?: ?Object,
): ?React.ComponentType<*> {
): ?React.ComponentType<any> {
info = info || this._subExtractor(index);
if (!info) {
return null;
Expand All @@ -322,48 +362,6 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
return null;
}

_computeState(props: Props<SectionT>): State {
const offset = props.ListHeaderComponent ? 1 : 0;
const stickyHeaderIndices = [];
const itemCount = props.sections.reduce((v, section) => {
stickyHeaderIndices.push(v + offset);
return v + section.data.length + 2; // Add two for the section header and footer.
}, 0);

return {
childProps: {
...props,
renderItem: this._renderItem,
ItemSeparatorComponent: undefined, // Rendered with renderItem
data: props.sections,
getItemCount: () => itemCount,
getItem,
keyExtractor: this._keyExtractor,
onViewableItemsChanged: props.onViewableItemsChanged
? this._onViewableItemsChanged
: undefined,
stickyHeaderIndices: props.stickySectionHeadersEnabled
? stickyHeaderIndices
: undefined,
},
};
}

constructor(props: Props<SectionT>, context: Object) {
super(props, context);
this.state = this._computeState(props);
}

UNSAFE_componentWillReceiveProps(nextProps: Props<SectionT>) {
this.setState(this._computeState(nextProps));
}

render() {
return (
<VirtualizedList {...this.state.childProps} ref={this._captureRef} />
);
}

_cellRefs = {};
_listRef: VirtualizedList;
_captureRef = ref => {
Expand All @@ -374,25 +372,40 @@ class VirtualizedSectionList<SectionT: SectionBase> extends React.PureComponent<
};
}

type ItemWithSeparatorProps = {
LeadingSeparatorComponent: ?React.ComponentType<*>,
SeparatorComponent: ?React.ComponentType<*>,
type ItemWithSeparatorCommonProps = $ReadOnly<{|
leadingItem: ?Item,
leadingSection: ?Object,
section: Object,
trailingItem: ?Item,
trailingSection: ?Object,
|}>;

type ItemWithSeparatorProps = $ReadOnly<{|
...ItemWithSeparatorCommonProps,
LeadingSeparatorComponent: ?React.ComponentType<any>,
SeparatorComponent: ?React.ComponentType<any>,
cellKey: string,
index: number,
item: Item,
onUpdateSeparator: (cellKey: string, newProps: Object) => void,
prevCellKey?: ?string,
renderItem: Function,
section: Object,
leadingItem: ?Item,
leadingSection: ?Object,
trailingItem: ?Item,
trailingSection: ?Object,
|}>;

type ItemWithSeparatorState = {
separatorProps: $ReadOnly<{|
highlighted: false,
...ItemWithSeparatorCommonProps,
|}>,
leadingSeparatorProps: $ReadOnly<{|
highlighted: false,
...ItemWithSeparatorCommonProps,
|}>,
};

class ItemWithSeparator extends React.Component<
ItemWithSeparatorProps,
$FlowFixMeState,
ItemWithSeparatorState,
> {
state = {
separatorProps: {
Expand Down Expand Up @@ -426,7 +439,7 @@ class ItemWithSeparator extends React.Component<
},
updateProps: (select: 'leading' | 'trailing', newProps: Object) => {
const {LeadingSeparatorComponent, cellKey, prevCellKey} = this.props;
if (select === 'leading' && LeadingSeparatorComponent) {
if (select === 'leading' && LeadingSeparatorComponent != null) {
this.setState(state => ({
leadingSeparatorProps: {...state.leadingSeparatorProps, ...newProps},
}));
Expand All @@ -439,25 +452,28 @@ class ItemWithSeparator extends React.Component<
},
};

UNSAFE_componentWillReceiveProps(props: ItemWithSeparatorProps) {
this.setState(state => ({
static getDerivedStateFromProps(
props: ItemWithSeparatorProps,
prevState: ItemWithSeparatorState,
): ?ItemWithSeparatorState {
return {
separatorProps: {
...this.state.separatorProps,
...prevState.separatorProps,
leadingItem: props.item,
leadingSection: props.leadingSection,
section: props.section,
trailingItem: props.trailingItem,
trailingSection: props.trailingSection,
},
leadingSeparatorProps: {
...this.state.leadingSeparatorProps,
...prevState.leadingSeparatorProps,
leadingItem: props.leadingItem,
leadingSection: props.leadingSection,
section: props.section,
trailingItem: props.item,
trailingSection: props.trailingSection,
},
}));
};
}

updateSeparatorProps(newProps: Object) {
Expand Down

0 comments on commit d513c4b

Please sign in to comment.