From 2022b1eee6fdf0dfabac6102ec5f304d2aef5516 Mon Sep 17 00:00:00 2001 From: Spencer Ahrens Date: Fri, 3 Mar 2017 13:14:04 -0800 Subject: [PATCH] Improve docs Reviewed By: hramos Differential Revision: D4649351 fbshipit-source-id: 06cbd735bdb51b6d9d4997a348cbc191193485aa --- Examples/UIExplorer/js/FlatListExample.js | 34 +++++++--- Examples/UIExplorer/js/SectionListExample.js | 29 ++++++--- Libraries/Components/ScrollView/ScrollView.js | 29 +++++---- .../CustomComponents/Lists/VirtualizedList.js | 63 +++++++++++++------ 4 files changed, 108 insertions(+), 47 deletions(-) diff --git a/Examples/UIExplorer/js/FlatListExample.js b/Examples/UIExplorer/js/FlatListExample.js index 5e422a9a4d2..9eae853bf92 100644 --- a/Examples/UIExplorer/js/FlatListExample.js +++ b/Examples/UIExplorer/js/FlatListExample.js @@ -77,7 +77,9 @@ class FlatListExample extends React.PureComponent { } render() { const filterRegex = new RegExp(String(this.state.filterText), 'i'); - const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title)); + const filter = (item) => ( + filterRegex.test(item.text) || filterRegex.test(item.title) + ); const filteredData = this.state.data.filter(filter); return ( } ) => { // Impressions can be logged here if (this.state.logViewable) { - infoLog('onViewableItemsChanged: ', info.changed.map((v) => ({...v, item: '...'}))); + infoLog( + 'onViewableItemsChanged: ', + info.changed.map((v) => ({...v, item: '...'})), + ); } }; _pressItem = (key: number) => { diff --git a/Examples/UIExplorer/js/SectionListExample.js b/Examples/UIExplorer/js/SectionListExample.js index fb7a38574a1..08ed9572d89 100644 --- a/Examples/UIExplorer/js/SectionListExample.js +++ b/Examples/UIExplorer/js/SectionListExample.js @@ -81,7 +81,9 @@ class SectionListExample extends React.PureComponent { }; render() { const filterRegex = new RegExp(String(this.state.filterText), 'i'); - const filter = (item) => (filterRegex.test(item.text) || filterRegex.test(item.title)); + const filter = (item) => ( + filterRegex.test(item.text) || filterRegex.test(item.title) + ); const filteredData = this.state.data.filter(filter); return ( } - ItemSeparatorComponent={() => } + SectionSeparatorComponent={() => + + } + ItemSeparatorComponent={() => + + } enableVirtualization={this.state.virtualized} onRefresh={() => alert('onRefresh: nothing to refresh :P')} onViewableItemsChanged={this._onViewableItemsChanged} @@ -117,8 +123,8 @@ class SectionListExample extends React.PureComponent { {title: 'Item In Header Section', text: 'Section s1', key: '0'}, ]}, {key: 's2', data: [ - {noImage: true, title: 'First item', text: 'Section s2', key: '0'}, - {noImage: true, title: 'Second item', text: 'Section s2', key: '1'}, + {noImage: true, title: '1st item', text: 'Section s2', key: '0'}, + {noImage: true, title: '2nd item', text: 'Section s2', key: '1'}, ]}, {key: 'Filtered Items', data: filteredData}, ]} @@ -127,11 +133,18 @@ class SectionListExample extends React.PureComponent { ); } - _renderItemComponent = ({item}) => ; - // This is called when items change viewability by scrolling into our out of the viewable area. + _renderItemComponent = ({item}) => ( + + ); + // This is called when items change viewability by scrolling into our out of + // the viewable area. _onViewableItemsChanged = (info: { changed: Array<{ - key: string, isViewable: boolean, item: {columns: Array<*>}, index: ?number, section?: any + key: string, + isViewable: boolean, + item: {columns: Array<*>}, + index: ?number, + section?: any }>}, ) => { // Impressions can be logged here diff --git a/Libraries/Components/ScrollView/ScrollView.js b/Libraries/Components/ScrollView/ScrollView.js index c8e3b36c9a4..71dffd9dc5e 100644 --- a/Libraries/Components/ScrollView/ScrollView.js +++ b/Libraries/Components/ScrollView/ScrollView.js @@ -48,19 +48,24 @@ const requireNativeComponent = require('requireNativeComponent'); * view from becoming the responder. * * - * `` vs `` - which one to use? - * ScrollView simply renders all its react child components at once. That - * makes it very easy to understand and use. - * On the other hand, this has a performance downside. Imagine you have a very - * long list of items you want to display, worth of couple of your ScrollView's - * heights. Creating JS components and native views upfront for all its items, - * which may not even be shown, will contribute to slow rendering of your - * screen and increased memory usage. + * `` vs [``](/react-native/docs/flatlist.html) - which one to use? * - * This is where ListView comes into play. ListView renders items lazily, - * just when they are about to appear. This laziness comes at cost of a more - * complicated API, which is worth it unless you are rendering a small fixed - * set of items. + * `ScrollView` simply renders all its react child components at once. That + * makes it very easy to understand and use. + * + * On the other hand, this has a performance downside. Imagine you have a very + * long list of items you want to display, maybe several screens worth of + * content. Creating JS components and native views for everythign all at once, + * much of which may not even be shown, will contribute to slow rendering and + * increased memory usage. + * + * This is where `FlatList` comes into play. `FlatList` renders items lazily, + * just when they are about to appear, and removes items that scroll way off + * screen to save memory and processing time. + * + * `FlatList` is also handy if you want to render separators between your items, + * multiple columns, infinite scroll loading, or any number of other features it + * supports out of the box. */ const ScrollView = React.createClass({ propTypes: { diff --git a/Libraries/CustomComponents/Lists/VirtualizedList.js b/Libraries/CustomComponents/Lists/VirtualizedList.js index ccc93ded507..5adb1a6fab5 100644 --- a/Libraries/CustomComponents/Lists/VirtualizedList.js +++ b/Libraries/CustomComponents/Lists/VirtualizedList.js @@ -49,19 +49,6 @@ import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper'; type Item = any; type renderItemType = (info: {item: Item, index: number}) => ?React.Element; -/** - * Renders a virtual list of items given a data blob and accessor functions. Items that are outside - * the render window (except for the initial items at the top) are 'virtualized' e.g. unmounted or - * never rendered in the first place. This improves performance and saves memory for large data - * sets, but will reset state on items that scroll too far out of the render window. - * - * TODO: Note that LayoutAnimation and sticky section headers both have bugs when used with this and - * are therefor not supported, but new Animated impl might work? - * https://github.com/facebook/react-native/pull/11315 - * - * TODO: removeClippedSubviews might not be necessary and may cause bugs? - * - */ type RequiredProps = { renderItem: renderItemType, /** @@ -76,7 +63,7 @@ type OptionalProps = { SeparatorComponent?: ?ReactClass, /** * `debug` will turn on extra logging and visual overlays to aid with debugging both usage and - * implementation. + * implementation, but with a significant perf hit. */ debug?: ?boolean, /** @@ -85,13 +72,28 @@ type OptionalProps = { * this for debugging purposes. */ disableVirtualization: boolean, - getItem: (items: any, index: number) => ?Item, - getItemCount: (items: any) => number, - getItemLayout?: (items: any, index: number) => + /** + * A generic accessor for extracting an item from any sort of data blob. + */ + getItem: (data: any, index: number) => ?Item, + /** + * Determines how many items are in the data blob. + */ + getItemCount: (data: any) => number, + getItemLayout?: (data: any, index: number) => {length: number, offset: number, index: number}, // e.g. height, y horizontal?: ?boolean, + /** + * How many items to render in the initial batch. This should be enough to fill the screen but not + * much more. + */ initialNumToRender: number, keyExtractor: (item: Item, index: number) => string, + /** + * The maximum number of items to render in each incremental render batch. The more rendered at + * once, the better the fill rate, but responsiveness my suffer because rendering content may + * interfere with responding to button taps or other interactions. + */ maxToRenderPerBatch: number, onEndReached?: ?(info: {distanceFromEnd: number}) => void, onEndReachedThreshold?: ?number, // units of visible length @@ -110,15 +112,34 @@ type OptionalProps = { * Set this true while waiting for new data from a refresh. */ refreshing?: ?boolean, + /** + * A native optimization that removes clipped subviews (those outside the parent) from the view + * hierarchy to offload work from the native rendering system. They are still kept around so no + * memory is saved and state is preserved. + */ removeClippedSubviews?: boolean, + /** + * Render a custom scroll component, e.g. with a differently styled `RefreshControl`. + */ renderScrollComponent: (props: Object) => React.Element, shouldItemUpdate: ( props: {item: Item, index: number}, nextProps: {item: Item, index: number} ) => boolean, + /** + * Amount of time between low-pri item render batches, e.g. for rendering items quite a ways off + * screen. Similar fill rate/responsiveness tradeoff as `maxToRenderPerBatch`. + */ updateCellsBatchingPeriod: number, viewabilityConfig?: ViewabilityConfig, - windowSize: number, // units of visible length + /** + * Determines the maximum number of items rendered outside of the visible area, in units of + * visible lengths. So if your list fills the screen, then `windowSize={21}` (the default) will + * render the visible screen area plus up to 10 screens above and 10 below the viewport. Reducing + * this number will reduce memory consumption and may improve performance, but will increase the + * chance that fast scrolling may reveal momentary blank areas of unrendered content. + */ + windowSize: number, }; export type Props = RequiredProps & OptionalProps; @@ -148,6 +169,12 @@ type State = {first: number, last: number}; * 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. + * + * NOTE: `LayoutAnimation` and sticky section headers both have bugs when used with this and are + * therefore not officially supported yet. + * + * NOTE: `removeClippedSubviews` might not be necessary and may cause bugs. If you see issues with + * content not rendering, try disabling it, and we may change the default there. */ class VirtualizedList extends React.PureComponent { props: Props;