mirror of
https://github.com/status-im/react-native.git
synced 2025-01-13 19:15:05 +00:00
Breaking API change - abandon ItemComponent
in favor of renderItem
Summary: After a fair bit of use, we have concluded that the `ItemComponent` mechanism is not worth the hassle. Flow has trouble type checking it thoroughly, requiring an 'item' prop is annoying, and it is very common to need to capture `this` anyway, e.g. for an `onPress` handler. A common pattern was something like: _renderItem = ({item}) => <MyItem foo={item.foo} onPress={() => this._onPress(item)} />}; ... ItemComponent={this._renderItem} which wouldn't flow check the props and doesn't benefit from reusing components. If we find some specific patterns that would benefit from the `ItemComponent` pattern, we can create a new component that provides that API and wraps `FlatList` under the hood. I'm going to do `SectionList` in a stacked diff. Reviewed By: bvaughn Differential Revision: D4625338 fbshipit-source-id: a4901f1c9d77e0115b0b8032b8c210f624e97ea3
This commit is contained in:
parent
f7d1060418
commit
2a1ab36257
@ -108,7 +108,6 @@ class FlatListExample extends React.PureComponent {
|
||||
<FlatList
|
||||
HeaderComponent={HeaderComponent}
|
||||
FooterComponent={FooterComponent}
|
||||
ItemComponent={this._renderItemComponent}
|
||||
SeparatorComponent={SeparatorComponent}
|
||||
data={filteredData}
|
||||
debug={this.state.debug}
|
||||
@ -122,6 +121,7 @@ class FlatListExample extends React.PureComponent {
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
ref={this._captureRef}
|
||||
refreshing={false}
|
||||
renderItem={this._renderItemComponent}
|
||||
shouldItemUpdate={this._shouldItemUpdate}
|
||||
viewabilityConfig={VIEWABILITY_CONFIG}
|
||||
/>
|
||||
|
@ -86,22 +86,16 @@ class ItemComponent extends React.PureComponent {
|
||||
}
|
||||
}
|
||||
|
||||
class StackedItemComponent extends React.PureComponent {
|
||||
props: {
|
||||
item: Item,
|
||||
};
|
||||
render() {
|
||||
const {item} = this.props;
|
||||
const itemHash = Math.abs(hashCode(item.title));
|
||||
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
|
||||
return (
|
||||
<View style={styles.stacked}>
|
||||
<Text style={styles.stackedText}>{item.title} - {item.text}</Text>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
</View>
|
||||
);
|
||||
}
|
||||
}
|
||||
const renderStackedItem = ({item}: {item: Item}) => {
|
||||
const itemHash = Math.abs(hashCode(item.title));
|
||||
const imgSource = THUMB_URLS[itemHash % THUMB_URLS.length];
|
||||
return (
|
||||
<View style={styles.stacked}>
|
||||
<Text style={styles.stackedText}>{item.title} - {item.text}</Text>
|
||||
<Image style={styles.thumb} source={imgSource} />
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
class FooterComponent extends React.PureComponent {
|
||||
render() {
|
||||
@ -287,9 +281,9 @@ module.exports = {
|
||||
ItemComponent,
|
||||
PlainInput,
|
||||
SeparatorComponent,
|
||||
StackedItemComponent,
|
||||
genItemData,
|
||||
getItemLayout,
|
||||
pressItem,
|
||||
renderSmallSwitchOption,
|
||||
renderStackedItem,
|
||||
};
|
||||
|
@ -99,7 +99,6 @@ class MultiColumnExample extends React.PureComponent {
|
||||
<FlatList
|
||||
FooterComponent={FooterComponent}
|
||||
HeaderComponent={HeaderComponent}
|
||||
ItemComponent={this._renderItemComponent}
|
||||
SeparatorComponent={SeparatorComponent}
|
||||
getItemLayout={this.state.fixedHeight ? this._getItemLayout : undefined}
|
||||
data={filteredData}
|
||||
@ -107,6 +106,7 @@ class MultiColumnExample extends React.PureComponent {
|
||||
numColumns={this.state.numColumns || 1}
|
||||
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
|
||||
refreshing={false}
|
||||
renderItem={this._renderItemComponent}
|
||||
shouldItemUpdate={this._shouldItemUpdate}
|
||||
disableVirtualization={!this.state.virtualized}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
|
@ -42,10 +42,10 @@ const {
|
||||
ItemComponent,
|
||||
PlainInput,
|
||||
SeparatorComponent,
|
||||
StackedItemComponent,
|
||||
genItemData,
|
||||
pressItem,
|
||||
renderSmallSwitchOption,
|
||||
renderStackedItem,
|
||||
} = require('./ListExampleShared');
|
||||
|
||||
const VIEWABILITY_CONFIG = {
|
||||
@ -54,7 +54,7 @@ const VIEWABILITY_CONFIG = {
|
||||
waitForInteraction: true,
|
||||
};
|
||||
|
||||
const SectionHeaderComponent = ({section}) => (
|
||||
const renderSectionHeader = ({section}) => (
|
||||
<View>
|
||||
<Text style={styles.headerText}>SECTION HEADER: {section.key}</Text>
|
||||
<SeparatorComponent />
|
||||
@ -104,16 +104,16 @@ class SectionListExample extends React.PureComponent {
|
||||
<SectionList
|
||||
ListHeaderComponent={HeaderComponent}
|
||||
ListFooterComponent={FooterComponent}
|
||||
ItemComponent={this._renderItemComponent}
|
||||
SectionHeaderComponent={SectionHeaderComponent}
|
||||
SectionSeparatorComponent={() => <CustomSeparatorComponent text="SECTION SEPARATOR" />}
|
||||
ItemSeparatorComponent={() => <CustomSeparatorComponent text="ITEM SEPARATOR" />}
|
||||
enableVirtualization={this.state.virtualized}
|
||||
onRefresh={() => alert('onRefresh: nothing to refresh :P')}
|
||||
onViewableItemsChanged={this._onViewableItemsChanged}
|
||||
refreshing={false}
|
||||
renderItem={this._renderItemComponent}
|
||||
renderSectionHeader={renderSectionHeader}
|
||||
sections={[
|
||||
{ItemComponent: StackedItemComponent, key: 's1', data: [
|
||||
{renderItem: renderStackedItem, key: 's1', data: [
|
||||
{title: 'Item In Header Section', text: 'Section s1', key: '0'},
|
||||
]},
|
||||
{key: 's2', data: [
|
||||
|
@ -43,14 +43,21 @@ import type {StyleObj} from 'StyleSheetTypes';
|
||||
import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper';
|
||||
import type {Props as VirtualizedListProps} from 'VirtualizedList';
|
||||
|
||||
type Item = any;
|
||||
|
||||
type RequiredProps<ItemT> = {
|
||||
/**
|
||||
* Note this can be a normal class component, or a functional component, such as a render method
|
||||
* on your main component.
|
||||
* Takes an item from `data` and renders it into the list. Typicaly usage:
|
||||
*
|
||||
* _renderItem = ({item}) => (
|
||||
* <TouchableOpacity onPress={() => this._onPress(item)}>
|
||||
* <Text>{item.title}}</Text>
|
||||
* <TouchableOpacity/>
|
||||
* );
|
||||
* ...
|
||||
* <FlatList data={[{title: 'Title Text'}]} renderItem={this._renderItem} />
|
||||
*
|
||||
* Provides additional metadata like `index` if you need it.
|
||||
*/
|
||||
ItemComponent: ReactClass<{item: ItemT, index: number}>,
|
||||
renderItem: ({item: ItemT, index: number}) => ?React.Element<*>,
|
||||
/**
|
||||
* For simplicity, data is just a plain array. If you want to use something else, like an
|
||||
* immutable list, use the underlying `VirtualizedList` directly.
|
||||
@ -125,8 +132,8 @@ type OptionalProps<ItemT> = {
|
||||
* Optional optimization to minimize re-rendering items.
|
||||
*/
|
||||
shouldItemUpdate: (
|
||||
prevProps: {item: ItemT, index: number},
|
||||
nextProps: {item: ItemT, index: number}
|
||||
prevInfo: {item: ItemT, index: number},
|
||||
nextInfo: {item: ItemT, index: number}
|
||||
) => boolean,
|
||||
/**
|
||||
* See ViewabilityHelper for flow type and comments.
|
||||
@ -159,7 +166,7 @@ type DefaultProps = typeof defaultProps;
|
||||
*
|
||||
* <FlatList
|
||||
* data={[{key: 'a', {key: 'b'}]}
|
||||
* ItemComponent={({item}) => <Text>{item.key}</Text>}
|
||||
* renderItem={({item}) => <Text>{item.key}</Text>}
|
||||
* />
|
||||
*/
|
||||
class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, void> {
|
||||
@ -186,7 +193,7 @@ class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, vo
|
||||
* Requires linear scan through data - use scrollToIndex instead if possible. May be janky without
|
||||
* `getItemLayout` prop.
|
||||
*/
|
||||
scrollToItem(params: {animated?: ?boolean, item: Item, viewPosition?: number}) {
|
||||
scrollToItem(params: {animated?: ?boolean, item: ItemT, viewPosition?: number}) {
|
||||
this._listRef.scrollToItem(params);
|
||||
}
|
||||
|
||||
@ -305,18 +312,21 @@ class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, vo
|
||||
}
|
||||
};
|
||||
|
||||
_renderItem = ({item, index}) => {
|
||||
const {ItemComponent, numColumns, columnWrapperStyle} = this.props;
|
||||
_renderItem = (info: {item: ItemT | Array<ItemT>, index: number}) => {
|
||||
const {renderItem, numColumns, columnWrapperStyle} = this.props;
|
||||
if (numColumns > 1) {
|
||||
const {item, index} = info;
|
||||
invariant(Array.isArray(item), 'Expected array of items with numColumns > 1');
|
||||
return (
|
||||
<View style={[{flexDirection: 'row'}, columnWrapperStyle]}>
|
||||
{item.map((it, kk) =>
|
||||
<ItemComponent key={kk} item={it} index={index * numColumns + kk} />)
|
||||
}
|
||||
{item.map((it, kk) => {
|
||||
const element = renderItem({item: it, index: index * numColumns + kk});
|
||||
return element && React.cloneElement(element, {key: kk});
|
||||
})}
|
||||
</View>
|
||||
);
|
||||
} else {
|
||||
return <ItemComponent item={item} index={index} />;
|
||||
return renderItem(info);
|
||||
}
|
||||
};
|
||||
|
||||
@ -340,7 +350,7 @@ class FlatList<ItemT> extends React.PureComponent<DefaultProps, Props<ItemT>, vo
|
||||
return (
|
||||
<VirtualizedList
|
||||
{...this.props}
|
||||
ItemComponent={this._renderItem}
|
||||
renderItem={this._renderItem}
|
||||
getItem={this._getItem}
|
||||
getItemCount={this._getItemCount}
|
||||
keyExtractor={this._keyExtractor}
|
||||
|
@ -43,9 +43,9 @@ type Item = any;
|
||||
|
||||
type NormalProps = {
|
||||
FooterComponent?: ReactClass<*>,
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
SectionHeaderComponent?: ReactClass<{info: Object}>,
|
||||
SeparatorComponent?: ReactClass<*>, // not supported yet
|
||||
renderItem: ({item: Item, index: number}) => ?React.Element<*>,
|
||||
renderSectionHeader?: ({section: Object}) => ?React.Element<*>,
|
||||
SeparatorComponent?: ?ReactClass<*>, // not supported yet
|
||||
|
||||
// Provide either `items` or `sections`
|
||||
items?: ?Array<Item>, // By default, an Item is assumed to be {key: string}
|
||||
@ -163,13 +163,12 @@ class MetroListView extends React.Component {
|
||||
}
|
||||
_renderFooter = () => <this.props.FooterComponent key="$footer" />;
|
||||
_renderRow = (item, sectionID, rowID, highlightRow) => {
|
||||
const {ItemComponent} = this.props;
|
||||
return <ItemComponent item={item} index={rowID} />;
|
||||
return this.props.renderItem({item, index: rowID});
|
||||
};
|
||||
_renderSectionHeader = (section, sectionID) => {
|
||||
const {SectionHeaderComponent} = this.props;
|
||||
invariant(SectionHeaderComponent, 'Must provide SectionHeaderComponent with sections prop');
|
||||
return <SectionHeaderComponent section={section} />;
|
||||
const {renderSectionHeader} = this.props;
|
||||
invariant(renderSectionHeader, 'Must provide renderSectionHeader with sections prop');
|
||||
return renderSectionHeader({section});
|
||||
}
|
||||
_renderSeparator = (sID, rID) => <this.props.SeparatorComponent key={sID + rID} />;
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ type SectionBase<SectionItemT> = {
|
||||
key: string,
|
||||
|
||||
// Optional props will override list-wide props just for this section.
|
||||
ItemComponent?: ?ReactClass<{item: SectionItemT, index: number}>,
|
||||
renderItem?: ?({item: SectionItemT, index: number}) => ?React.Element<*>,
|
||||
SeparatorComponent?: ?ReactClass<*>,
|
||||
keyExtractor?: (item: SectionItemT) => string,
|
||||
|
||||
@ -55,10 +55,6 @@ type SectionBase<SectionItemT> = {
|
||||
// FooterComponent?: ?ReactClass<*>,
|
||||
// HeaderComponent?: ?ReactClass<*>,
|
||||
// onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void,
|
||||
|
||||
// TODO: support recursive sections
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: SectionBase<*>}>,
|
||||
// sections?: ?Array<Section>;
|
||||
};
|
||||
|
||||
type RequiredProps<SectionT: SectionBase<*>> = {
|
||||
@ -67,9 +63,9 @@ type RequiredProps<SectionT: SectionBase<*>> = {
|
||||
|
||||
type OptionalProps<SectionT: SectionBase<*>> = {
|
||||
/**
|
||||
* Default renderer for every item in every section.
|
||||
* Default renderer for every item in every section. Can be over-ridden on a per-section basis.
|
||||
*/
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
renderItem: ({item: Item, index: number}) => ?React.Element<*>,
|
||||
/**
|
||||
* Rendered in between adjacent Items within each section.
|
||||
*/
|
||||
@ -85,7 +81,7 @@ type OptionalProps<SectionT: SectionBase<*>> = {
|
||||
/**
|
||||
* Rendered at the top of each section. Sticky headers are not yet supported.
|
||||
*/
|
||||
SectionHeaderComponent?: ?ReactClass<{section: SectionT}>,
|
||||
renderSectionHeader?: ?({section: SectionT}) => ?React.Element<*>,
|
||||
/**
|
||||
* Rendered in between each section.
|
||||
*/
|
||||
@ -93,7 +89,7 @@ type OptionalProps<SectionT: SectionBase<*>> = {
|
||||
/**
|
||||
* 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
|
||||
* stored outside of the recursive `ItemComponent` instance tree.
|
||||
* stored outside of the recursive `renderItem` instance tree.
|
||||
*/
|
||||
enableVirtualization?: ?boolean,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
|
@ -47,7 +47,7 @@ const {computeWindowedRenderLimits} = require('VirtualizeUtils');
|
||||
import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper';
|
||||
|
||||
type Item = any;
|
||||
type ItemComponentType = ReactClass<{item: Item, index: number}>;
|
||||
type renderItemType = ({item: Item, index: number}) => ?React.Element<*>;
|
||||
|
||||
/**
|
||||
* Renders a virtual list of items given a data blob and accessor functions. Items that are outside
|
||||
@ -63,7 +63,7 @@ type ItemComponentType = ReactClass<{item: Item, index: number}>;
|
||||
*
|
||||
*/
|
||||
type RequiredProps = {
|
||||
ItemComponent: ItemComponentType,
|
||||
renderItem: renderItemType,
|
||||
/**
|
||||
* 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.
|
||||
@ -633,7 +633,7 @@ class CellRenderer extends React.Component {
|
||||
onLayout: (event: Object, cellKey: string, index: number) => void,
|
||||
onUnmount: (cellKey: string) => void,
|
||||
parentProps: {
|
||||
ItemComponent: ItemComponentType,
|
||||
renderItem: renderItemType,
|
||||
getItemLayout?: ?Function,
|
||||
shouldItemUpdate: (
|
||||
props: {item: Item, index: number},
|
||||
@ -654,8 +654,9 @@ class CellRenderer extends React.Component {
|
||||
}
|
||||
render() {
|
||||
const {item, index, parentProps} = this.props;
|
||||
const {ItemComponent, getItemLayout} = parentProps;
|
||||
const element = <ItemComponent item={item} index={index} />;
|
||||
const {renderItem, getItemLayout} = parentProps;
|
||||
invariant(renderItem, 'no renderItem!');
|
||||
const element = renderItem({item, index});
|
||||
if (getItemLayout && !parentProps.debug) {
|
||||
return element;
|
||||
}
|
||||
|
@ -51,7 +51,7 @@ type SectionBase = {
|
||||
key: string,
|
||||
|
||||
// Optional props will override list-wide props just for this section.
|
||||
ItemComponent?: ?ReactClass<{item: SectionItem, index: number}>,
|
||||
renderItem?: ?({item: SectionItem, index: number}) => ?React.Element<*>,
|
||||
SeparatorComponent?: ?ReactClass<*>,
|
||||
keyExtractor?: (item: SectionItem) => string,
|
||||
|
||||
@ -59,10 +59,6 @@ type SectionBase = {
|
||||
// FooterComponent?: ?ReactClass<*>,
|
||||
// HeaderComponent?: ?ReactClass<*>,
|
||||
// onViewableItemsChanged?: ({viewableItems: Array<ViewToken>, changed: Array<ViewToken>}) => void,
|
||||
|
||||
// TODO: support recursive sections
|
||||
// SectionHeaderComponent?: ?ReactClass<{section: SectionBase}>,
|
||||
// sections?: ?Array<Section>;
|
||||
};
|
||||
|
||||
type RequiredProps<SectionT: SectionBase> = {
|
||||
@ -73,15 +69,19 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
/**
|
||||
* Rendered after the last item in the last section.
|
||||
*/
|
||||
FooterComponent?: ?ReactClass<*>,
|
||||
ListFooterComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
* Rendered at the very beginning of the list.
|
||||
*/
|
||||
ListHeaderComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
* Default renderer for every item in every section.
|
||||
*/
|
||||
ItemComponent: ReactClass<{item: Item, index: number}>,
|
||||
renderItem: ({item: Item, index: number}) => ?React.Element<*>,
|
||||
/**
|
||||
* Rendered at the top of each section. In the future, a sticky option will be added.
|
||||
*/
|
||||
SectionHeaderComponent?: ?ReactClass<{section: SectionT}>,
|
||||
renderSectionHeader?: ?({section: SectionT}) => ?React.Element<*>,
|
||||
/**
|
||||
* Rendered at the bottom of every Section, except the very last one, in place of the normal
|
||||
* SeparatorComponent.
|
||||
@ -90,11 +90,11 @@ type OptionalProps<SectionT: SectionBase> = {
|
||||
/**
|
||||
* Rendered at the bottom of every Item except the very last one in the last section.
|
||||
*/
|
||||
SeparatorComponent?: ?ReactClass<*>,
|
||||
ItemSeparatorComponent?: ?ReactClass<*>,
|
||||
/**
|
||||
* 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
|
||||
* stored outside of the recursive `ItemComponent` instance tree.
|
||||
* stored outside of the recursive `renderItem` instance tree.
|
||||
*/
|
||||
enableVirtualization?: ?boolean,
|
||||
keyExtractor: (item: Item, index: number) => string,
|
||||
@ -220,14 +220,16 @@ class VirtualizedSectionList<SectionT: SectionBase>
|
||||
if (!info) {
|
||||
return null;
|
||||
} else if (info.index == null) {
|
||||
const {SectionHeaderComponent} = this.props;
|
||||
return SectionHeaderComponent ? <SectionHeaderComponent section={info.section} /> : null;
|
||||
const {renderSectionHeader} = this.props;
|
||||
return renderSectionHeader ? renderSectionHeader({section: info.section}) : null;
|
||||
} else {
|
||||
const ItemComponent = info.section.ItemComponent || this.props.ItemComponent;
|
||||
const renderItem = info.section.renderItem ||
|
||||
this.props.renderItem;
|
||||
const SeparatorComponent = this._getSeparatorComponent(index, info);
|
||||
invariant(renderItem, 'no renderItem!');
|
||||
return (
|
||||
<View>
|
||||
<ItemComponent item={item} index={info.index} />
|
||||
{renderItem({item, index: info.index || 0})}
|
||||
{SeparatorComponent && <SeparatorComponent />}
|
||||
</View>
|
||||
);
|
||||
@ -239,7 +241,7 @@ class VirtualizedSectionList<SectionT: SectionBase>
|
||||
if (!info) {
|
||||
return null;
|
||||
}
|
||||
const SeparatorComponent = info.section.SeparatorComponent || this.props.SeparatorComponent;
|
||||
const SeparatorComponent = info.section.SeparatorComponent || this.props.ItemSeparatorComponent;
|
||||
const {SectionSeparatorComponent} = this.props;
|
||||
const isLastItemInList = index === this.state.childProps.getItemCount() - 1;
|
||||
const isLastItemInSection = info.index === info.section.data.length - 1;
|
||||
@ -265,8 +267,10 @@ class VirtualizedSectionList<SectionT: SectionBase>
|
||||
return {
|
||||
childProps: {
|
||||
...props,
|
||||
ItemComponent: this._renderItem,
|
||||
SeparatorComponent: undefined, // Rendered with ItemComponent
|
||||
FooterComponent: this.props.ListFooterComponent,
|
||||
HeaderComponent: this.props.ListHeaderComponent,
|
||||
renderItem: this._renderItem,
|
||||
SeparatorComponent: undefined, // Rendered with renderItem
|
||||
data: props.sections,
|
||||
getItemCount: () => itemCount,
|
||||
getItem,
|
||||
|
@ -14,64 +14,77 @@
|
||||
const FlatList = require('FlatList');
|
||||
const React = require('react');
|
||||
|
||||
class MyListItem extends React.Component {
|
||||
props: {
|
||||
item: {
|
||||
title: string,
|
||||
},
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
function renderMyListItem(info: {item: {title: string}, index: number}) {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
testBadDataWithTypicalItemComponent(): React.Element<*> {
|
||||
testEverythingIsFine() {
|
||||
const data = [{
|
||||
title: 'Title Text',
|
||||
key: 1,
|
||||
}];
|
||||
return <FlatList renderItem={renderMyListItem} data={data} />;
|
||||
},
|
||||
|
||||
testBadDataWithTypicalItem() {
|
||||
// $FlowExpectedError - bad title type 6, should be string
|
||||
const data = [{
|
||||
title: 6,
|
||||
key: 1,
|
||||
}];
|
||||
return <FlatList ItemComponent={MyListItem} data={data} />;
|
||||
return <FlatList renderItem={renderMyListItem} data={data} />;
|
||||
},
|
||||
|
||||
testMissingFieldWithTypicalItemComponent(): React.Element<*> {
|
||||
testMissingFieldWithTypicalItem() {
|
||||
const data = [{
|
||||
key: 1,
|
||||
}];
|
||||
// $FlowExpectedError - missing title
|
||||
return <FlatList ItemComponent={MyListItem} data={data} />;
|
||||
return <FlatList renderItem={renderMyListItem} data={data} />;
|
||||
},
|
||||
|
||||
testGoodDataWithGoodCustomItemComponentFunction() {
|
||||
testGoodDataWithBadCustomRenderItemFunction() {
|
||||
const data = [{
|
||||
widgetCount: 3,
|
||||
widget: 6,
|
||||
key: 1,
|
||||
}];
|
||||
return (
|
||||
<FlatList
|
||||
ItemComponent={(props: {widgetCount: number}): React.Element<*> =>
|
||||
<MyListItem item={{title: props.widgetCount + ' Widgets'}} />
|
||||
renderItem={(info) =>
|
||||
// $FlowExpectedError - bad widgetCount type 6, should be Object
|
||||
<span>{info.item.widget.missingProp}</span>
|
||||
}
|
||||
data={data}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
||||
testBadNonInheritedDefaultProp(): React.Element<*> {
|
||||
const data = [];
|
||||
// $FlowExpectedError - bad numColumns type "lots"
|
||||
return <FlatList ItemComponent={MyListItem} data={data} numColumns="lots" />;
|
||||
testBadRenderItemFunction() {
|
||||
const data = [{
|
||||
title: 'foo',
|
||||
key: 1,
|
||||
}];
|
||||
return [
|
||||
// $FlowExpectedError - title should be inside `item`
|
||||
<FlatList renderItem={(info: {title: string}) => <span /> } data={data} />,
|
||||
// $FlowExpectedError - bad index type string, should be number
|
||||
<FlatList renderItem={(info: {item: any, index: string}) => <span /> } data={data} />,
|
||||
// $FlowExpectedError - bad title type number, should be string
|
||||
<FlatList renderItem={(info: {item: {title: number}}) => <span /> } data={data} />,
|
||||
// EverythingIsFine
|
||||
<FlatList renderItem={(info: {item: {title: string}}) => <span /> } data={data} />,
|
||||
];
|
||||
},
|
||||
|
||||
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} />;
|
||||
testOtherBadProps() {
|
||||
return [
|
||||
// $FlowExpectedError - bad numColumns type "lots"
|
||||
<FlatList renderItem={renderMyListItem} data={[]} numColumns="lots" />,
|
||||
// $FlowExpectedError - bad windowSize type "big"
|
||||
<FlatList renderItem={renderMyListItem} data={[]} windowSize="big" />,
|
||||
// $FlowExpectedError - missing `data` prop
|
||||
<FlatList renderItem={renderMyListItem} />,
|
||||
];
|
||||
},
|
||||
};
|
||||
|
@ -14,55 +14,49 @@
|
||||
const React = require('react');
|
||||
const SectionList = require('SectionList');
|
||||
|
||||
class MyListItem extends React.Component {
|
||||
props: {
|
||||
item: {
|
||||
title: string,
|
||||
},
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
function renderMyListItem(info: {item: {title: string}, index: number}) {
|
||||
return <span />;
|
||||
}
|
||||
|
||||
class MyHeader extends React.Component {
|
||||
props: {
|
||||
section: {
|
||||
fooNumber: number,
|
||||
}
|
||||
};
|
||||
render() {
|
||||
return <span />;
|
||||
}
|
||||
}
|
||||
const renderMyHeader = ({section}: {section: {fooNumber: number} & Object}) => <span />;
|
||||
|
||||
module.exports = {
|
||||
testGoodDataWithGoodCustomItemComponentFunction() {
|
||||
testGoodDataWithGoodItem() {
|
||||
const sections = [{
|
||||
key: 'a', data: [{
|
||||
widgetCount: 3,
|
||||
title: 'foo',
|
||||
key: 1,
|
||||
}],
|
||||
}];
|
||||
return (
|
||||
<SectionList
|
||||
ItemComponent={(props: {widgetCount: number}): React.Element<*> =>
|
||||
<MyListItem item={{title: props.widgetCount + ' Widgets'}} />
|
||||
}
|
||||
sections={sections}
|
||||
/>
|
||||
);
|
||||
return <SectionList renderItem={renderMyListItem} sections={sections} />;
|
||||
},
|
||||
|
||||
testBadRenderItemFunction() {
|
||||
const sections = [{
|
||||
key: 'a', data: [{
|
||||
title: 'foo',
|
||||
key: 1,
|
||||
}],
|
||||
}];
|
||||
return [
|
||||
// $FlowExpectedError - title should be inside `item`
|
||||
<SectionList renderItem={(info: {title: string}) => <span /> } sections={sections} />,
|
||||
// $FlowExpectedError - bad index type string, should be number
|
||||
<SectionList renderItem={(info: {index: string}) => <span /> } sections={sections} />,
|
||||
// EverythingIsFine
|
||||
<SectionList renderItem={(info: {item: {title: string}}) => <span /> } sections={sections} />,
|
||||
];
|
||||
},
|
||||
|
||||
testBadInheritedDefaultProp(): React.Element<*> {
|
||||
const sections = [];
|
||||
// $FlowExpectedError - bad windowSize type "big"
|
||||
return <SectionList ItemComponent={MyListItem} sections={sections} windowSize="big" />;
|
||||
return <SectionList renderItem={renderMyListItem} sections={sections} windowSize="big" />;
|
||||
},
|
||||
|
||||
testMissingData(): React.Element<*> {
|
||||
// $FlowExpectedError - missing `sections` prop
|
||||
return <SectionList ItemComponent={MyListItem} />;
|
||||
return <SectionList renderItem={renderMyListItem} />;
|
||||
},
|
||||
|
||||
testBadSectionsShape(): React.Element<*> {
|
||||
@ -73,7 +67,7 @@ module.exports = {
|
||||
}],
|
||||
}];
|
||||
// $FlowExpectedError - section missing `data` field
|
||||
return <SectionList ItemComponent={MyListItem} sections={sections} />;
|
||||
return <SectionList renderItem={renderMyListItem} sections={sections} />;
|
||||
},
|
||||
|
||||
testBadSectionsMetadata(): React.Element<*> {
|
||||
@ -86,8 +80,8 @@ module.exports = {
|
||||
}];
|
||||
return (
|
||||
<SectionList
|
||||
SectionHeaderComponent={MyHeader}
|
||||
ItemComponent={MyListItem}
|
||||
renderSectionHeader={renderMyHeader}
|
||||
renderItem={renderMyListItem}
|
||||
sections={sections}
|
||||
/>
|
||||
);
|
||||
|
Loading…
x
Reference in New Issue
Block a user