mirror of
https://github.com/status-im/react-native.git
synced 2025-02-24 23:28:12 +00:00
Improve flow typing
Summary: - Properly inherit flow types from base components, including `defaultProps` - template-ify the `Item` type as it flows from the `data` prop into `ItemComponent` Note that for `SectionList` is is harder to do the `Item` typing because each section in the `sections` array can have a different `Item` type, plus all the optional overrides...not sure how to tackle that. Reviewed By: yungsters Differential Revision: D4557523 fbshipit-source-id: a0c5279fcdaabe6aab5fe11743a99c3715a44032
This commit is contained in:
parent
c529a06cb8
commit
63d3ea17a7
@ -44,6 +44,7 @@ suppress_type=$FixMe
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-9]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-9]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
|
||||
suppress_comment=\\(.\\|\n\\)*\\$FlowExpectedError
|
||||
|
||||
unsafe.enable_getters_and_setters=true
|
||||
|
||||
|
@ -108,7 +108,7 @@ class FlatListExample extends React.PureComponent {
|
||||
key={(this.state.horizontal ? 'h' : 'v') + (this.state.fixedHeight ? 'f' : 'd')}
|
||||
legacyImplementation={false}
|
||||
numColumns={1}
|
||||
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
|
||||
onRefresh={this._onRefresh}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
ref={this._captureRef}
|
||||
refreshing={false}
|
||||
@ -121,6 +121,7 @@ class FlatListExample extends React.PureComponent {
|
||||
_getItemLayout = (data: any, index: number) => {
|
||||
return getItemLayout(data, index, this.state.horizontal);
|
||||
};
|
||||
_onRefresh = () => alert('onRefresh: nothing to refresh :P');
|
||||
_renderItemComponent = ({item}) => {
|
||||
return (
|
||||
<ItemComponent
|
||||
@ -154,7 +155,7 @@ class FlatListExample extends React.PureComponent {
|
||||
_pressItem = (key: number) => {
|
||||
pressItem(this, key);
|
||||
};
|
||||
_listRef: FlatList;
|
||||
_listRef: FlatList<*>;
|
||||
}
|
||||
|
||||
|
||||
|
@ -41,22 +41,23 @@ const invariant = require('invariant');
|
||||
|
||||
import type {StyleObj} from 'StyleSheetTypes';
|
||||
import type {Viewable} from 'ViewabilityHelper';
|
||||
import type {Props as VirtualizedListProps} from 'VirtualizedList';
|
||||
|
||||
type Item = any;
|
||||
|
||||
type RequiredProps = {
|
||||
type RequiredProps<ItemT> = {
|
||||
/**
|
||||
* Note this can be a normal class component, or a functional component, such as a render method
|
||||
* on your main component.
|
||||
*/
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
ItemComponent: ReactClass<{item: ItemT, index: number}>,
|
||||
/**
|
||||
* For simplicity, data is just a plain array. If you want to use something else, like an
|
||||
* immutable list, use the underlying `VirtualizedList` directly.
|
||||
*/
|
||||
data: ?Array<Item>,
|
||||
data: ?Array<ItemT>,
|
||||
};
|
||||
type OptionalProps = {
|
||||
type OptionalProps<ItemT> = {
|
||||
/**
|
||||
* Rendered at the bottom of all the items.
|
||||
*/
|
||||
@ -79,7 +80,7 @@ type OptionalProps = {
|
||||
* Remember to include separator length (height or width) in your offset calculation if you
|
||||
* specify `SeparatorComponent`.
|
||||
*/
|
||||
getItemLayout?: (data: ?Array<Item>, index: number) =>
|
||||
getItemLayout?: (data: ?Array<ItemT>, index: number) =>
|
||||
{length: number, offset: number, index: number},
|
||||
/**
|
||||
* If true, renders items next to each other horizontally instead of stacked vertically.
|
||||
@ -90,7 +91,7 @@ type OptionalProps = {
|
||||
* and as the react key to track item re-ordering. The default extractor checks item.key, then
|
||||
* falls back to using the index, like react does.
|
||||
*/
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
keyExtractor: (item: ItemT, index: number) => string,
|
||||
/**
|
||||
* Multiple columns can only be rendered with horizontal={false} and will zig-zag like a flexWrap
|
||||
* layout. Items should all be the same height - masonry layouts are not supported.
|
||||
@ -111,6 +112,7 @@ type OptionalProps = {
|
||||
* `viewablePercentThreshold` prop.
|
||||
*/
|
||||
onViewableItemsChanged?: ?({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
legacyImplementation?: ?boolean,
|
||||
/**
|
||||
* Set this true while waiting for new data from a refresh.
|
||||
*/
|
||||
@ -123,11 +125,19 @@ type OptionalProps = {
|
||||
* Optional optimization to minimize re-rendering items.
|
||||
*/
|
||||
shouldItemUpdate: (
|
||||
prevProps: {item: Item, index: number},
|
||||
nextProps: {item: Item, index: number}
|
||||
prevProps: {item: ItemT, index: number},
|
||||
nextProps: {item: ItemT, index: number}
|
||||
) => boolean,
|
||||
};
|
||||
type Props = RequiredProps & OptionalProps; // plus props from the underlying implementation
|
||||
type Props<ItemT> = RequiredProps<ItemT> & OptionalProps<ItemT> & VirtualizedListProps;
|
||||
|
||||
const defaultProps = {
|
||||
...VirtualizedList.defaultProps,
|
||||
getItem: undefined,
|
||||
getItemCount: undefined,
|
||||
numColumns: 1,
|
||||
};
|
||||
type DefaultProps = typeof defaultProps;
|
||||
|
||||
/**
|
||||
* A performant interface for rendering simple, flat lists, supporting the most handy features:
|
||||
@ -148,13 +158,9 @@ type Props = RequiredProps & OptionalProps; // plus props from the underlying im
|
||||
* ItemComponent={({item}) => <Text>{item.key}</Text>}
|
||||
* />
|
||||
*/
|
||||
class FlatList extends React.PureComponent {
|
||||
static defaultProps = {
|
||||
keyExtractor: VirtualizedList.defaultProps.keyExtractor,
|
||||
numColumns: 1,
|
||||
shouldItemUpdate: VirtualizedList.defaultProps.shouldItemUpdate,
|
||||
};
|
||||
props: Props;
|
||||
class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, void> {
|
||||
static defaultProps: DefaultProps = defaultProps;
|
||||
props: Props<ItemT>;
|
||||
/**
|
||||
* Scrolls to the end of the content. May be janky without getItemLayout prop.
|
||||
*/
|
||||
@ -191,7 +197,7 @@ class FlatList extends React.PureComponent {
|
||||
this._checkProps(this.props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
componentWillReceiveProps(nextProps: Props<ItemT>) {
|
||||
this._checkProps(nextProps);
|
||||
}
|
||||
|
||||
@ -200,7 +206,7 @@ class FlatList extends React.PureComponent {
|
||||
|
||||
_captureRef = (ref) => { this._listRef = ref; };
|
||||
|
||||
_checkProps(props: Props) {
|
||||
_checkProps(props: Props<ItemT>) {
|
||||
const {
|
||||
getItem,
|
||||
getItemCount,
|
||||
@ -229,7 +235,7 @@ class FlatList extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
_getItem = (data: Array<Item>, index: number): Item | Array<Item> => {
|
||||
_getItem = (data: Array<ItemT>, index: number): ItemT | Array<ItemT> => {
|
||||
const {numColumns} = this.props;
|
||||
if (numColumns > 1) {
|
||||
const ret = [];
|
||||
@ -243,13 +249,19 @@ class FlatList extends React.PureComponent {
|
||||
}
|
||||
};
|
||||
|
||||
_getItemCount = (data: Array<Item>): number => {
|
||||
_getItemCount = (data: Array<ItemT>): number => {
|
||||
return Math.floor(data.length / this.props.numColumns);
|
||||
};
|
||||
|
||||
_keyExtractor = (items: Item | Array<Item>, index: number): string => {
|
||||
_keyExtractor = (items: ItemT | Array<ItemT>, index: number): string => {
|
||||
const {keyExtractor, numColumns} = this.props;
|
||||
if (numColumns > 1) {
|
||||
invariant(
|
||||
Array.isArray(items),
|
||||
'FlatList: Encountered internal consistency error, expected each item to consist of an ' +
|
||||
'array with 1-%s columns; instead, received a single item.',
|
||||
numColumns,
|
||||
);
|
||||
return items.map((it, kk) => keyExtractor(it, index * numColumns + kk)).join(':');
|
||||
} else {
|
||||
return keyExtractor(items, index);
|
||||
|
@ -49,7 +49,7 @@ type NormalProps = {
|
||||
|
||||
// Provide either `items` or `sections`
|
||||
items?: ?Array<Item>, // By default, an Item is assumed to be {key: string}
|
||||
sections?: ?Array<{key: string, items: Array<Item>}>,
|
||||
sections?: ?Array<{key: string, data: Array<Item>}>,
|
||||
|
||||
/**
|
||||
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
|
||||
@ -146,7 +146,7 @@ class MetroListView extends React.Component {
|
||||
const sections = {};
|
||||
props.sections.forEach((sectionIn, ii) => {
|
||||
const sectionID = 's' + ii;
|
||||
sections[sectionID] = sectionIn.itemData;
|
||||
sections[sectionID] = sectionIn.data;
|
||||
sectionHeaderData[sectionID] = sectionIn;
|
||||
});
|
||||
return {
|
||||
|
@ -37,19 +37,19 @@ const React = require('React');
|
||||
const VirtualizedSectionList = require('VirtualizedSectionList');
|
||||
|
||||
import type {Viewable} from 'ViewabilityHelper';
|
||||
import type {Props as VirtualizedSectionListProps} from 'VirtualizedSectionList';
|
||||
|
||||
type Item = any;
|
||||
type SectionItem = any;
|
||||
|
||||
type SectionBase = {
|
||||
type SectionBase<SectionItemT> = {
|
||||
// Must be provided directly on each section.
|
||||
data: Array<SectionItem>,
|
||||
data: Array<SectionItemT>,
|
||||
key: string,
|
||||
|
||||
// Optional props will override list-wide props just for this section.
|
||||
ItemComponent?: ?ReactClass<{item: SectionItem, index: number}>,
|
||||
ItemComponent?: ?ReactClass<{item: SectionItemT, index: number}>,
|
||||
SeparatorComponent?: ?ReactClass<*>,
|
||||
keyExtractor?: (item: SectionItem) => string,
|
||||
keyExtractor?: (item: SectionItemT) => string,
|
||||
|
||||
// TODO: support more optional/override props
|
||||
// FooterComponent?: ?ReactClass<*>,
|
||||
@ -57,15 +57,15 @@ type SectionBase = {
|
||||
// onViewableItemsChanged?: ({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
|
||||
// TODO: support recursive sections
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: SectionBase}>,
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: SectionBase<*>}>,
|
||||
// sections?: ?Array<Section>;
|
||||
};
|
||||
|
||||
type RequiredProps<SectionT: SectionBase> = {
|
||||
type RequiredProps<SectionT: SectionBase<*>> = {
|
||||
sections: Array<SectionT>,
|
||||
};
|
||||
|
||||
type OptionalProps<SectionT: SectionBase> = {
|
||||
type OptionalProps<SectionT: SectionBase<*>> = {
|
||||
/**
|
||||
* Rendered after the last item in the last section.
|
||||
*/
|
||||
@ -73,7 +73,7 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
/**
|
||||
* Default renderer for every item in every section.
|
||||
*/
|
||||
ItemComponent?: ?ReactClass<{item: Item, index: number}>,
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
/**
|
||||
* Rendered at the top of each section. In the future, a sticky option will be added.
|
||||
*/
|
||||
@ -93,8 +93,8 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
* stored outside of the recursive `ItemComponent` instance tree.
|
||||
*/
|
||||
enableVirtualization?: ?boolean,
|
||||
keyExtractor?: (item: Item) => string,
|
||||
onEndReached?: ({distanceFromEnd: number}) => void,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
onEndReached?: ?({distanceFromEnd: number}) => void,
|
||||
/**
|
||||
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
|
||||
* sure to also set the `refreshing` prop correctly.
|
||||
@ -104,21 +104,25 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
* Called when the viewability of rows changes, as defined by the
|
||||
* `viewablePercentThreshold` prop.
|
||||
*/
|
||||
onViewableItemsChanged?: ({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
onViewableItemsChanged?: ?({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
/**
|
||||
* Set this true while waiting for new data from a refresh.
|
||||
*/
|
||||
refreshing?: boolean,
|
||||
refreshing?: ?boolean,
|
||||
/**
|
||||
* This is an optional optimization to minimize re-rendering items.
|
||||
*/
|
||||
shouldItemUpdate?: (
|
||||
shouldItemUpdate: (
|
||||
prevProps: {item: Item, index: number},
|
||||
nextProps: {item: Item, index: number}
|
||||
) => boolean,
|
||||
};
|
||||
|
||||
type Props<SectionT> = RequiredProps<SectionT> & OptionalProps<SectionT>;
|
||||
type Props<SectionT> = RequiredProps<SectionT>
|
||||
& OptionalProps<SectionT>
|
||||
& VirtualizedSectionListProps<SectionT>;
|
||||
|
||||
type DefaultProps = typeof VirtualizedSectionList.defaultProps;
|
||||
|
||||
/**
|
||||
* A performant interface for rendering sectioned lists, supporting the most handy features:
|
||||
@ -132,8 +136,11 @@ type Props<SectionT> = RequiredProps<SectionT> & OptionalProps<SectionT>;
|
||||
*
|
||||
* If you don't need section support and want a simpler interface, use FlatList.
|
||||
*/
|
||||
class SectionList<SectionT: SectionBase> extends React.Component<void, Props<SectionT>, void> {
|
||||
class SectionList<SectionT: SectionBase<*>>
|
||||
extends React.PureComponent<DefaultProps, Props<SectionT>, *>
|
||||
{
|
||||
props: Props<SectionT>;
|
||||
static defaultProps: DefaultProps = VirtualizedSectionList.defaultProps;
|
||||
|
||||
render() {
|
||||
if (this.props.legacyImplementation) {
|
||||
|
@ -68,7 +68,7 @@ type RequiredProps = {
|
||||
* The default accessor functions assume this is an Array<{key: string}> but you can override
|
||||
* getItem, getItemCount, and keyExtractor to handle any type of index-based data.
|
||||
*/
|
||||
data: any,
|
||||
data?: any,
|
||||
};
|
||||
type OptionalProps = {
|
||||
FooterComponent?: ?ReactClass<*>,
|
||||
@ -89,12 +89,12 @@ type OptionalProps = {
|
||||
getItemCount: (items: any) => number,
|
||||
getItemLayout?: (items: any, index: number) =>
|
||||
{length: number, offset: number, index: number}, // e.g. height, y
|
||||
horizontal: boolean,
|
||||
horizontal?: ?boolean,
|
||||
initialNumToRender: number,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
maxToRenderPerBatch: number,
|
||||
onEndReached: ({distanceFromEnd: number}) => void,
|
||||
onEndReachedThreshold: number, // units of visible length
|
||||
onEndReached?: ?({distanceFromEnd: number}) => void,
|
||||
onEndReachedThreshold?: ?number, // units of visible length
|
||||
onLayout?: ?Function,
|
||||
/**
|
||||
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
|
||||
@ -105,11 +105,11 @@ type OptionalProps = {
|
||||
* Called when the viewability of rows changes, as defined by the
|
||||
* `viewablePercentThreshold` prop.
|
||||
*/
|
||||
onViewableItemsChanged?: ({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
onViewableItemsChanged?: ?({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
/**
|
||||
* Set this true while waiting for new data from a refresh.
|
||||
*/
|
||||
refreshing?: boolean,
|
||||
refreshing?: ?boolean,
|
||||
removeClippedSubviews?: boolean,
|
||||
renderScrollComponent: (props: Object) => React.Element<*>,
|
||||
shouldItemUpdate: (
|
||||
@ -126,11 +126,11 @@ type OptionalProps = {
|
||||
viewablePercentThreshold: number,
|
||||
windowSize: number, // units of visible length
|
||||
};
|
||||
type Props = RequiredProps & OptionalProps;
|
||||
export type Props = RequiredProps & OptionalProps;
|
||||
|
||||
let _usedIndexForKey = false;
|
||||
|
||||
class VirtualizedList extends React.PureComponent {
|
||||
class VirtualizedList extends React.PureComponent<OptionalProps, Props, *> {
|
||||
props: Props;
|
||||
|
||||
// scrollToEnd may be janky without getItemLayout prop
|
||||
@ -182,7 +182,7 @@ class VirtualizedList extends React.PureComponent {
|
||||
);
|
||||
}
|
||||
|
||||
static defaultProps: OptionalProps = {
|
||||
static defaultProps = {
|
||||
disableVirtualization: false,
|
||||
getItem: (data: any, index: number) => data[index],
|
||||
getItemCount: (data: any) => data ? data.length : 0,
|
||||
@ -390,7 +390,12 @@ class VirtualizedList extends React.PureComponent {
|
||||
|
||||
_onCellLayout = (e, cellKey, index) => {
|
||||
const layout = e.nativeEvent.layout;
|
||||
const next = {offset: this._selectOffset(layout), length: this._selectLength(layout), index, inLayout: true};
|
||||
const next = {
|
||||
offset: this._selectOffset(layout),
|
||||
length: this._selectLength(layout),
|
||||
index,
|
||||
inLayout: true,
|
||||
};
|
||||
const curr = this._frames[cellKey];
|
||||
if (!curr ||
|
||||
next.offset !== curr.offset ||
|
||||
|
@ -40,11 +40,12 @@ const invariant = require('invariant');
|
||||
const warning = require('warning');
|
||||
|
||||
import type {Viewable} from 'ViewabilityHelper';
|
||||
import type {Props as VirtualizedListProps} from 'VirtualizedList';
|
||||
|
||||
type Item = any;
|
||||
type SectionItem = any;
|
||||
|
||||
type Section = {
|
||||
type SectionBase = {
|
||||
// Must be provided directly on each section.
|
||||
data: Array<SectionItem>,
|
||||
key: string,
|
||||
@ -60,17 +61,35 @@ type Section = {
|
||||
// onViewableItemsChanged?: ({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
|
||||
// TODO: support recursive sections
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: Section}>,
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: SectionBase}>,
|
||||
// sections?: ?Array<Section>;
|
||||
}
|
||||
|
||||
type RequiredProps = {
|
||||
sections: Array<Section>,
|
||||
};
|
||||
type OptionalProps = {
|
||||
ItemComponent?: ?ReactClass<{item: Item, index: number}>,
|
||||
SectionHeaderComponent?: ?ReactClass<{section: Section}>,
|
||||
|
||||
type RequiredProps<SectionT: SectionBase> = {
|
||||
sections: Array<SectionT>,
|
||||
};
|
||||
|
||||
type OptionalProps<SectionT: SectionBase> = {
|
||||
/**
|
||||
* Rendered after the last item in the last section.
|
||||
*/
|
||||
FooterComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
* Default renderer for every item in every section.
|
||||
*/
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
SeparatorComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
* Warning: Virtualization can drastically improve memory consumption for long lists, but trashes
|
||||
@ -78,42 +97,65 @@ type OptionalProps = {
|
||||
* stored outside of the recursive `ItemComponent` instance tree.
|
||||
*/
|
||||
enableVirtualization?: ?boolean,
|
||||
horizontal?: ?boolean,
|
||||
keyExtractor?: (item: Item, index: number) => string,
|
||||
onEndReached?: ({distanceFromEnd: number}) => void,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
onEndReached?: ?({distanceFromEnd: number}) => void,
|
||||
/**
|
||||
* If provided, a standard RefreshControl will be added for "Pull to Refresh" functionality. Make
|
||||
* sure to also set the `refreshing` prop correctly.
|
||||
*/
|
||||
onRefresh?: ?Function,
|
||||
/**
|
||||
* Called when the viewability of rows changes, as defined by the
|
||||
* `viewablePercentThreshold` prop. Called for all items from all sections.
|
||||
* `viewablePercentThreshold` prop.
|
||||
*/
|
||||
onViewableItemsChanged?: ({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
onViewableItemsChanged?: ?({viewableItems: Array<Viewable>, changed: Array<Viewable>}) => void,
|
||||
/**
|
||||
* 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 = RequiredProps & OptionalProps;
|
||||
|
||||
export type Props<SectionT> =
|
||||
RequiredProps<SectionT> &
|
||||
OptionalProps<SectionT> &
|
||||
VirtualizedListProps;
|
||||
|
||||
type DefaultProps = (typeof VirtualizedList.defaultProps) & {data: Array<Item>};
|
||||
type State = {childProps: VirtualizedListProps};
|
||||
|
||||
/**
|
||||
* Right now this just flattens everything into one list and uses VirtualizedList under the
|
||||
* hood. The only operation that might not scale well is concatting the data arrays of all the
|
||||
* sections when new props are received, which should be plenty fast for up to ~10,000 items.
|
||||
*/
|
||||
class VirtualizedSectionList extends React.PureComponent {
|
||||
props: Props;
|
||||
class VirtualizedSectionList<SectionT: SectionBase>
|
||||
extends React.PureComponent<DefaultProps, Props<SectionT>, State>
|
||||
{
|
||||
props: Props<SectionT>;
|
||||
|
||||
state: {
|
||||
childProps: Object,
|
||||
};
|
||||
state: State;
|
||||
|
||||
static defaultProps: OptionalProps = {
|
||||
keyExtractor: (item: Item, index: number) => item.key || String(index),
|
||||
static defaultProps: DefaultProps = {
|
||||
...VirtualizedList.defaultProps,
|
||||
data: [],
|
||||
};
|
||||
|
||||
_keyExtractor = (item: Item, index: number) => {
|
||||
const info = this._subExtractor(index);
|
||||
return info && info.key;
|
||||
return (info && info.key) || String(index);
|
||||
};
|
||||
|
||||
_subExtractor(
|
||||
index: number,
|
||||
): ?{
|
||||
section: Section,
|
||||
section: SectionT,
|
||||
key: string, // Key of the section or combined key for section + item
|
||||
index: ?number, // Relative index within the section
|
||||
} {
|
||||
@ -221,9 +263,10 @@ class VirtualizedSectionList extends React.PureComponent {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
_computeState(props: Props) {
|
||||
_computeState(props: Props<SectionT>): State {
|
||||
const itemCount = props.sections.reduce((v, section) => v + section.data.length + 1, 0);
|
||||
return {
|
||||
childProps: {
|
||||
@ -242,7 +285,7 @@ class VirtualizedSectionList extends React.PureComponent {
|
||||
};
|
||||
}
|
||||
|
||||
constructor(props: Props, context: Object) {
|
||||
constructor(props: Props<SectionT>, context: Object) {
|
||||
super(props, context);
|
||||
warning(
|
||||
!props.stickySectionHeadersEnabled,
|
||||
@ -251,7 +294,7 @@ class VirtualizedSectionList extends React.PureComponent {
|
||||
this.state = this._computeState(props);
|
||||
}
|
||||
|
||||
componentWillReceiveProps(nextProps: Props) {
|
||||
componentWillReceiveProps(nextProps: Props<SectionT>) {
|
||||
this.setState(this._computeState(nextProps));
|
||||
}
|
||||
|
||||
|
77
Libraries/Experimental/__flowtests__/FlatList-flowtest.js
Normal file
77
Libraries/Experimental/__flowtests__/FlatList-flowtest.js
Normal file
@ -0,0 +1,77 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const FlatList = require('FlatList');
|
||||
const React = require('react');
|
||||
|
||||
class MyListItem extends React.Component {
|
||||
props: {
|
||||
item: {
|
||||
title: string,
|
||||
},
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testBadDataWithTypicalItemComponent(): React.Element<*> {
|
||||
// $FlowExpectedError - bad title type 6, should be string
|
||||
const data = [{
|
||||
title: 6,
|
||||
key: 1,
|
||||
}];
|
||||
return <FlatList ItemComponent={MyListItem} data={data} />;
|
||||
},
|
||||
|
||||
testMissingFieldWithTypicalItemComponent(): React.Element<*> {
|
||||
const data = [{
|
||||
key: 1,
|
||||
}];
|
||||
// $FlowExpectedError - missing title
|
||||
return <FlatList ItemComponent={MyListItem} data={data} />;
|
||||
},
|
||||
|
||||
testGoodDataWithGoodCustomItemComponentFunction() {
|
||||
const data = [{
|
||||
widgetCount: 3,
|
||||
key: 1,
|
||||
}];
|
||||
return (
|
||||
<FlatList
|
||||
ItemComponent={(props: {widgetCount: number}): React.Element<*> =>
|
||||
<MyListItem item={{title: props.widgetCount + ' Widgets'}} />
|
||||
}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
testBadNonInheritedDefaultProp(): React.Element<*> {
|
||||
const data = [];
|
||||
// $FlowExpectedError - bad numColumns type "lots"
|
||||
return <FlatList ItemComponent={MyListItem} data={data} numColumns="lots" />;
|
||||
},
|
||||
|
||||
testBadInheritedDefaultProp(): React.Element<*> {
|
||||
const data = [];
|
||||
// $FlowExpectedError - bad windowSize type "big"
|
||||
return <FlatList ItemComponent={MyListItem} data={data} windowSize="big" />;
|
||||
},
|
||||
|
||||
testMissingData(): React.Element<*> {
|
||||
// $FlowExpectedError - missing `data` prop
|
||||
return <FlatList ItemComponent={MyListItem} />;
|
||||
},
|
||||
};
|
95
Libraries/Experimental/__flowtests__/SectionList-flowtest.js
Normal file
95
Libraries/Experimental/__flowtests__/SectionList-flowtest.js
Normal file
@ -0,0 +1,95 @@
|
||||
/**
|
||||
* Copyright (c) 2013-present, Facebook, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license found in the
|
||||
* LICENSE file in the root directory of this source tree. An additional grant
|
||||
* of patent rights can be found in the PATENTS file in the same directory.
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
|
||||
'use strict';
|
||||
|
||||
const React = require('react');
|
||||
const SectionList = require('SectionList');
|
||||
|
||||
class MyListItem extends React.Component {
|
||||
props: {
|
||||
item: {
|
||||
title: string,
|
||||
},
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
|
||||
class MyHeader extends React.Component {
|
||||
props: {
|
||||
section: {
|
||||
fooNumber: number,
|
||||
}
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testGoodDataWithGoodCustomItemComponentFunction() {
|
||||
const sections = [{
|
||||
key: 'a', data: [{
|
||||
widgetCount: 3,
|
||||
key: 1,
|
||||
}],
|
||||
}];
|
||||
return (
|
||||
<SectionList
|
||||
ItemComponent={(props: {widgetCount: number}): React.Element<*> =>
|
||||
<MyListItem item={{title: props.widgetCount + ' Widgets'}} />
|
||||
}
|
||||
sections={sections}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
testBadInheritedDefaultProp(): React.Element<*> {
|
||||
const sections = [];
|
||||
// $FlowExpectedError - bad windowSize type "big"
|
||||
return <SectionList ItemComponent={MyListItem} sections={sections} windowSize="big" />;
|
||||
},
|
||||
|
||||
testMissingData(): React.Element<*> {
|
||||
// $FlowExpectedError - missing `sections` prop
|
||||
return <SectionList ItemComponent={MyListItem} />;
|
||||
},
|
||||
|
||||
testBadSectionsShape(): React.Element<*> {
|
||||
const sections = [{
|
||||
key: 'a', items: [{
|
||||
title: 'foo',
|
||||
key: 1,
|
||||
}],
|
||||
}];
|
||||
// $FlowExpectedError - section missing `data` field
|
||||
return <SectionList ItemComponent={MyListItem} sections={sections} />;
|
||||
},
|
||||
|
||||
testBadSectionsMetadata(): React.Element<*> {
|
||||
// $FlowExpectedError - section has bad meta data `fooNumber` field of type string
|
||||
const sections = [{
|
||||
key: 'a', fooNumber: 'string', data: [{
|
||||
title: 'foo',
|
||||
key: 1,
|
||||
}],
|
||||
}];
|
||||
return (
|
||||
<SectionList
|
||||
SectionHeaderComponent={MyHeader}
|
||||
ItemComponent={MyListItem}
|
||||
sections={sections}
|
||||
/>
|
||||
);
|
||||
},
|
||||
};
|
Loading…
x
Reference in New Issue
Block a user