diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index d72228d33ceaa2..c340f679198e64 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -6,6 +6,7 @@ * * @flow * @format + * @generate-docs */ 'use strict'; @@ -30,62 +31,94 @@ import type {RenderItemType, RenderItemProps} from './VirtualizedList'; type RequiredProps = {| /** - * For simplicity, data is just a plain array. If you want to use something else, like an - * immutable list, use the underlying `VirtualizedList` directly. + For simplicity, data is a plain array. If you want to use something else, + like an immutable list, use the underlying + [`VirtualizedList`](virtualizedlist.md) directly. */ data: ?$ReadOnlyArray, |}; type OptionalProps = {| /** - * Takes an item from `data` and renders it into the list. Example usage: - * - * ( - * - * )} - * data={[{title: 'Title Text', key: 'item1'}]} - * renderItem={({item, separators}) => ( - * this._onPress(item)} - * onShowUnderlay={separators.highlight} - * onHideUnderlay={separators.unhighlight}> - * - * {item.title} - * - * - * )} - * /> - * - * Provides additional metadata like `index` if you need it, as well as a more generic - * `separators.updateProps` function which let's you set whatever props you want to change the - * rendering of either the leading separator or trailing separator in case the more common - * `highlight` and `unhighlight` (which set the `highlighted: boolean` prop) are insufficient for - * your use-case. + ```jsx + renderItem({ item, index, separators }); + ``` + + Takes an item from `data` and renders it into the list. + + Provides additional metadata like `index` if you need it, as well as a more + generic `separators.updateProps` function which let you set whatever props + you want to change the rendering of either the leading separator or trailing + separator in case the more common `highlight` and `unhighlight` (which set + the `highlighted: boolean` prop) are insufficient for your use case. + + - `item` (Object): The item from `data` being rendered. + - `index` (number): The index corresponding to this item in the `data` + array. + - `separators` (Object) + - `highlight` (Function) + - `unhighlight` (Function) + - `updateProps` (Function) + - `select` (enum('leading', 'trailing')) + - `newProps` (Object) + + Example usage: + + ```jsx + ( + + )} + data={[{title: 'Title Text', key: 'item1'}]} + renderItem={({item, index, separators}) => ( + this._onPress(item)} + onShowUnderlay={separators.highlight} + onHideUnderlay={separators.unhighlight}> + + {item.title} + + + )} + /> + ``` */ renderItem?: ?RenderItemType, /** - * Optional custom style for multi-item rows generated when numColumns > 1. + Optional custom style for multi-item rows generated when `numColumns > 1`. + + @type style object */ columnWrapperStyle?: ViewStyleProp, /** - * A marker property for telling the list to re-render (since it implements `PureComponent`). If - * any of your `renderItem`, Header, Footer, etc. functions depend on anything outside of the - * `data` prop, stick it here and treat it immutably. + A marker property for telling the list to re-render (since it implements + `PureComponent`). If any of your `renderItem`, Header, Footer, etc. + functions depend on anything outside of the `data` prop, stick it here and + treat it immutably. + + @type function */ extraData?: any, /** - * `getItemLayout` is an optional optimizations that let us skip measurement of dynamic content if - * you know the height of items a priori. `getItemLayout` is the most efficient, and is easy to - * use if you have fixed height items, for example: - * - * getItemLayout={(data, index) => ( - * {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index} - * )} - * - * Adding `getItemLayout` can be a great performance boost for lists of several hundred items. - * Remember to include separator length (height or width) in your offset calculation if you - * specify `ItemSeparatorComponent`. + ```jsx + (data, index) => {length: number, offset: number, index: number} + ``` + + `getItemLayout` is an optional optimization that allows skipping the + measurement of dynamic content if you know the size (height or width) of + items ahead of time. `getItemLayout` is efficient if you have fixed size + items, for example: + + ```jsx + getItemLayout={(data, index) => ( + {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index} + )} + ``` + + Adding `getItemLayout` can be a great performance boost for lists of several + hundred items. Remember to include separator length (height or width) in + your offset calculation if you specify `ItemSeparatorComponent`. */ getItemLayout?: ( data: ?Array, @@ -97,39 +130,54 @@ type OptionalProps = {| ... }, /** - * If true, renders items next to each other horizontally instead of stacked vertically. + If true, renders items next to each other horizontally instead of stacked + vertically. */ horizontal?: ?boolean, /** - * How many items to render in the initial batch. This should be enough to fill the screen but not - * much more. Note these items will never be unmounted as part of the windowed rendering in order - * to improve perceived performance of scroll-to-top actions. + How many items to render in the initial batch. This should be enough to fill + the screen but not much more. Note these items will never be unmounted as + part of the windowed rendering in order to improve perceived performance of + scroll-to-top actions. */ initialNumToRender: number, /** - * Instead of starting at the top with the first item, start at `initialScrollIndex`. This - * disables the "scroll to top" optimization that keeps the first `initialNumToRender` items - * always rendered and immediately renders the items starting at this initial index. Requires - * `getItemLayout` to be implemented. + Instead of starting at the top with the first item, start at + `initialScrollIndex`. This disables the "scroll to top" optimization that + keeps the first `initialNumToRender` items always rendered and immediately + renders the items starting at this initial index. Requires `getItemLayout` + to be implemented. */ initialScrollIndex?: ?number, /** - * Reverses the direction of scroll. Uses scale transforms of -1. + Reverses the direction of scroll. Uses scale transforms of `-1`. */ inverted?: ?boolean, /** - * Used to extract a unique key for a given item at the specified index. Key is used for caching - * 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. + ```jsx + (item: object, index: number) => string; + ``` + + Used to extract a unique key for a given item at the specified index. Key is + used for caching 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: 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. + 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. */ numColumns: number, /** - * See `ScrollView` for flow type and further documentation. + Fades out the edges of the the list content. + + If the value is greater than 0, the fading edges will be set accordingly to + the current scroll direction and position, indicating if there is more + content to show. + + @platform android */ fadingEdgeLength?: ?number, |}; @@ -161,146 +209,283 @@ const defaultProps = { ...VirtualizedList.defaultProps, numColumns: 1, /** - * Enabling this prop on Android greatly improves scrolling performance with no known issues. - * The alternative is that scrolling on Android is unusably bad. Enabling it on iOS has a few - * known issues. + This may improve scroll performance for large lists. On Android the default + value is true + + > Note: May have bugs (missing content) in some circumstances - use at your + > own risk. */ removeClippedSubviews: Platform.OS === 'android', }; export type DefaultProps = typeof defaultProps; /** - * A performant interface for rendering simple, flat lists, supporting the most handy features: - * - * - Fully cross-platform. - * - Optional horizontal mode. - * - Configurable viewability callbacks. - * - Header support. - * - Footer support. - * - Separator support. - * - Pull to Refresh. - * - Scroll loading. - * - ScrollToIndex support. - * - * If you need section support, use [``](docs/sectionlist.html). - * - * Minimal Example: - * - * {item.key}} - * /> - * - * More complex, multi-select example demonstrating `PureComponent` usage for perf optimization and avoiding bugs. - * - * - By binding the `onPressItem` handler, the props will remain `===` and `PureComponent` will - * prevent wasteful re-renders unless the actual `id`, `selected`, or `title` props change, even - * if the components rendered in `MyListItem` did not have such optimizations. - * - By passing `extraData={this.state}` to `FlatList` we make sure `FlatList` itself will re-render - * when the `state.selected` changes. Without setting this prop, `FlatList` would not know it - * needs to re-render any items because it is also a `PureComponent` and the prop comparison will - * not show any changes. - * - `keyExtractor` tells the list to use the `id`s for the react keys instead of the default `key` property. - * - * - * class MyListItem extends React.PureComponent { - * _onPress = () => { - * this.props.onPressItem(this.props.id); - * }; - * - * render() { - * const textColor = this.props.selected ? "red" : "black"; - * return ( - * - * - * - * {this.props.title} - * - * - * - * ); - * } - * } - * - * class MultiSelectList extends React.PureComponent { - * state = {selected: (new Map(): Map)}; - * - * _keyExtractor = (item, index) => item.id; - * - * _onPressItem = (id: string) => { - * // updater functions are preferred for transactional updates - * this.setState((state) => { - * // copy the map rather than modifying state. - * const selected = new Map(state.selected); - * selected.set(id, !selected.get(id)); // toggle - * return {selected}; - * }); - * }; - * - * _renderItem = ({item}) => ( - * - * ); - * - * render() { - * return ( - * - * ); - * } - * } - * - * This is a convenience wrapper around [``](docs/virtualizedlist.html), - * and thus inherits its props (as well as those of `ScrollView`) that aren't explicitly listed - * here, along with the following caveats: - * - * - Internal state is not preserved when content scrolls out of the render window. Make sure all - * your data is captured in the item data or external stores like Flux, Redux, or Relay. - * - This is a `PureComponent` which means that it will not re-render if `props` remain shallow- - * equal. Make sure that everything your `renderItem` function depends on is passed as a prop - * (e.g. `extraData`) that is not `===` after updates, otherwise your UI may not update on - * changes. This includes the `data` prop and parent component state. - * - In order to constrain memory and enable smooth scrolling, content is rendered asynchronously - * offscreen. This means it's possible to scroll faster than the fill rate ands momentarily see - * blank content. This is a tradeoff that can be adjusted to suit the needs of each application, - * and we are working on improving it behind the scenes. - * - By default, the list looks for a `key` prop on each item and uses that for the React key. - * Alternatively, you can provide a custom `keyExtractor` prop. - * - * Also inherits [ScrollView Props](docs/scrollview.html#props), unless it is nested in another FlatList of same orientation. + A performant interface for rendering basic, flat lists, supporting the most + handy features: + + - Fully cross-platform. + - Optional horizontal mode. + - Configurable viewability callbacks. + - Header support. + - Footer support. + - Separator support. + - Pull to Refresh. + - Scroll loading. + - ScrollToIndex support. + - Multiple column support. + + If you need section support, use [``](sectionlist.md). + + ```SnackPlayer name=flatlist-simple + import React from 'react'; + import { SafeAreaView, View, FlatList, StyleSheet, Text } from 'react-native'; + import Constants from 'expo-constants'; + + const DATA = [ + { + id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', + title: 'First Item', + }, + { + id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63', + title: 'Second Item', + }, + { + id: '58694a0f-3da1-471f-bd96-145571e29d72', + title: 'Third Item', + }, + ]; + + const Item = ({ title }) => { + return ( + + {title} + + ); + } + + const App = () => { + const renderItem = ({ item }) => ( + + ); + + return ( + + item.id} + /> + + ); + } + + const styles = StyleSheet.create({ + container: { + flex: 1, + marginTop: Constants.statusBarHeight, + }, + item: { + backgroundColor: '#f9c2ff', + padding: 20, + marginVertical: 8, + marginHorizontal: 16, + }, + title: { + fontSize: 32, + }, + }); + + export default App; + ``` + + To render multiple columns, use the [`numColumns`](flatlist.md#numcolumns) + prop. Using this approach instead of a `flexWrap` layout can prevent conflicts + with the item height logic. + + More complex, multi-select example demonstrating `` usage for perf + optimization and avoiding bugs. + + - By passing `extraData={selected}` to `FlatList` we make sure `FlatList` + itself will re-render when the state changes. Without setting this prop, + `FlatList` would not know it needs to re-render any items because it is a + `PureComponent` and the prop comparison will not show any changes. + - `keyExtractor` tells the list to use the `id`s for the react keys instead of + the default `key` property. + + ```SnackPlayer name=flatlist-selectable + import React from 'react'; + import { + SafeAreaView, + TouchableOpacity, + FlatList, + StyleSheet, + Text, + } from 'react-native'; + import Constants from 'expo-constants'; + + const DATA = [ + { + id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba', + title: 'First Item', + }, + { + id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63', + title: 'Second Item', + }, + { + id: '58694a0f-3da1-471f-bd96-145571e29d72', + title: 'Third Item', + }, + ]; + + const Item = ({ id, title, selected, onSelect }) => { + return ( + onSelect(id)} + style={[ + styles.item, + { backgroundColor: selected ? '#6e3b6e' : '#f9c2ff' }, + ]} + > + {title} + + ); + } + + const App = () => { + const [selected, setSelected] = React.useState(new Map()); + + const onSelect = React.useCallback( + id => { + const newSelected = new Map(selected); + newSelected.set(id, !selected.get(id)); + + setSelected(newSelected); + }, + [selected], + ); + + const renderItem = ({ item }) => ( + + ); + + return ( + + item.id} + extraData={selected} + /> + + ); + } + + const styles = StyleSheet.create({ + container: { + flex: 1, + marginTop: Constants.statusBarHeight, + }, + item: { + backgroundColor: '#f9c2ff', + padding: 20, + marginVertical: 8, + marginHorizontal: 16, + }, + title: { + fontSize: 32, + }, + }); + + export default App; + ``` + + This is a convenience wrapper around + [``](virtualizedlist.md), and thus inherits its props (as + well as those of [``](scrollview.md)) that aren't explicitly + listed here, along with the following caveats: + + - Internal state is not preserved when content scrolls out of the render + window. Make sure all your data is captured in the item data or external + stores like Flux, Redux, or Relay. + - This is a `PureComponent` which means that it will not re-render if `props` + remain shallow-equal. Make sure that everything your `renderItem` function + depends on is passed as a prop (e.g. `extraData`) that is not `===` after + updates, otherwise your UI may not update on changes. This includes the + `data` prop and parent component state. + - In order to constrain memory and enable smooth scrolling, content is + rendered asynchronously offscreen. This means it's possible to scroll faster + than the fill rate and momentarily see blank content. This is a tradeoff + that can be adjusted to suit the needs of each application, and we are + working on improving it behind the scenes. + - By default, the list looks for a `key` prop on each item and uses that for + the React key. Alternatively, you can provide a custom `keyExtractor` prop. + + Inherits [ScrollView Props](scrollview.md#props), unless it is nested in + another FlatList of same orientation. */ class FlatList extends React.PureComponent, void> { static defaultProps: DefaultProps = defaultProps; props: Props; /** - * Scrolls to the end of the content. May be janky without `getItemLayout` prop. + ```jsx + scrollToEnd([params]); + ``` + + Scrolls to the end of the content. May be janky without `getItemLayout` + prop. */ - scrollToEnd(params?: ?{animated?: ?boolean, ...}) { + scrollToEnd( + params?: ?{ + /** + Whether the list should do an animation while scrolling. Defaults to + `true`. + */ + animated?: ?boolean, + ... + }, + ) { if (this._listRef) { this._listRef.scrollToEnd(params); } } /** - * Scrolls to the item at the specified index such that it is positioned in the viewable area - * such that `viewPosition` 0 places it at the top, 1 at the bottom, and 0.5 centered in the - * middle. `viewOffset` is a fixed number of pixels to offset the final target position. - * - * Note: cannot scroll to locations outside the render window without specifying the - * `getItemLayout` prop. + ```jsx + scrollToIndex(params); + ``` + + Scrolls to the item at the specified index such that it is positioned in the + viewable area such that `viewPosition` 0 places it at the top, 1 at the + bottom, and 0.5 centered in the middle. + + > Note: Cannot scroll to locations outside the render window without + > specifying the `getItemLayout` prop. */ scrollToIndex(params: { + /** + Whether the list should do an animation while scrolling. Defaults to + `true`. + */ animated?: ?boolean, + /** + The index to scroll to. Required. + */ index: number, + /** + A fixed number of pixels to offset the final target position. + */ viewOffset?: number, + /** + A value of `0` places the item specified by index at the top, `1` at the + bottom, and `0.5` centered in the middle. + */ viewPosition?: number, ... }) { @@ -310,14 +495,28 @@ class FlatList extends React.PureComponent, void> { } /** - * Requires linear scan through data - use `scrollToIndex` instead if possible. - * - * Note: cannot scroll to locations outside the render window without specifying the - * `getItemLayout` prop. + ```jsx + scrollToItem(params); + ``` + + Requires linear scan through data - use `scrollToIndex` instead if possible. + + > Note: Cannot scroll to locations outside the render window without + > specifying the `getItemLayout` prop. */ scrollToItem(params: { + /** + Whether the list should do an animation while scrolling. Defaults to + `true`. + */ animated?: ?boolean, + /** + The item to scroll to. Required. + */ item: ItemT, + /** + missing + */ viewPosition?: number, ... }) { @@ -327,20 +526,39 @@ class FlatList extends React.PureComponent, void> { } /** - * Scroll to a specific content pixel offset in the list. - * - * Check out [scrollToOffset](docs/virtualizedlist.html#scrolltooffset) of VirtualizedList + ```jsx + scrollToOffset(params); + ``` + + Scroll to a specific content pixel offset in the list. */ - scrollToOffset(params: {animated?: ?boolean, offset: number, ...}) { + scrollToOffset(params: { + /** + The offset to scroll to. In case of `horizontal` being true, the offset is + the x-value, in any other case the offset is the y-value. Required. + */ + animated?: ?boolean, + /** + Whether the list should do an animation while scrolling. Defaults to + `true`. + */ + offset: number, + ... + }) { if (this._listRef) { this._listRef.scrollToOffset(params); } } /** - * Tells the list an interaction has occurred, which should trigger viewability calculations, e.g. - * if `waitForInteractions` is true and the user has not scrolled. This is typically called by - * taps on items or by navigation actions. + ```jsx + recordInteraction(); + ``` + + Tells the list an interaction has occurred, which should trigger viewability + calculations, e.g. if `waitForInteractions` is true and the user has not + scrolled. This is typically called by taps on items or by navigation + actions. */ recordInteraction() { if (this._listRef) { @@ -349,9 +567,11 @@ class FlatList extends React.PureComponent, void> { } /** - * Displays the scroll indicators momentarily. - * - * @platform ios + ```jsx + flashScrollIndicators(); + ``` + + Displays the scroll indicators momentarily. */ flashScrollIndicators() { if (this._listRef) { @@ -360,7 +580,11 @@ class FlatList extends React.PureComponent, void> { } /** - * Provides a handle to the underlying scroll responder. + ```jsx + getScrollResponder(); + ``` + + Provides a handle to the underlying scroll responder. */ getScrollResponder(): ?ScrollResponderType { if (this._listRef) { @@ -369,7 +593,11 @@ class FlatList extends React.PureComponent, void> { } /** - * Provides a reference to the underlying host component + ```jsx + getNativeScrollRef(); + ``` + + Provides a reference to the underlying scroll component */ getNativeScrollRef(): | ?React.ElementRef @@ -379,6 +607,13 @@ class FlatList extends React.PureComponent, void> { } } + /** + ```jsx + getScrollableNode(); + ``` + + Provides a handle to the underlying scroll node. + */ getScrollableNode(): any { if (this._listRef) { return this._listRef.getScrollableNode();