ReactNative組件學習FlatList

NO IMAGE

高性能的簡單列表組件,支持下面這些常用的功能:

  • 完全跨平臺。
  • 支持水平佈局模式。
  • 行組件顯示或隱藏時可配置回調事件。
  • 支持單獨的頭部組件。
  • 支持單獨的尾部組件。
  • 支持自定義行間分隔線。
  • 支持下拉刷新。
  • 支持上拉加載。
  • 支持跳轉到指定行(ScrollToIndex)。

如果需要分組/類/區(section),請使用<SectionList>.

一個最簡單的例子:

<FlatList
  data={[{key: 'a'}, {key: 'b'}]}
  renderItem={({item}) => <Text>{item.key}</Text>}
/>

下面是一個較複雜的例子,其中演示瞭如何利用PureComponent來進一步優化性能和減少 bug 產生的可能(以下這段文字需要你深刻理解 shouldComponentUpdate 的機制,以及 Component 和 PureComponent 的不同,所以如果不瞭解就先跳過吧)。

  • 對於MyListItem組件來說,其onPressItem屬性使用箭頭函數而非 bind 的方式進行綁定,使其不會在每次列表重新 render 時生成一個新的函數,從而保證了 props 的不變性(當然前提是 idselectedtitle也沒變),不會觸發自身無謂的重新 render。換句話說,如果你是用 bind 來綁定onPressItem,每次都會生成一個新的函數,導致 props 在===比較時返回 false,從而觸發自身的一次不必要的重新 render。

  • FlatList指定extraData={this.state}屬性,是為了保證state.selected變化時,能夠正確觸發FlatList的更新。如果不指定此屬性,則FlatList不會觸發更新,因為它是一個PureComponent,其 props 在===比較中沒有變化則不會觸發更新。

  • keyExtractor屬性指定使用 id 作為列表每一項的 key。

    class MyListItem extends React.PureComponent {
    _onPress = () => {
    this.props.onPressItem(this.props.id);
    };

    render() {
      const textColor = this.props.selected ? "red" : "black";
      return (
        <TouchableOpacity onPress={this._onPress}>
          <View>
            <Text style={{ color: textColor }}>
              {this.props.title}
            </Text>
          </View>
        </TouchableOpacity>
      );
    }
    

    }

    class MultiSelectList extends React.PureComponent {
    state = {selected: (new Map(): Map<string, boolean>)};

    _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}) => (
      <MyListItem
        id={item.id}
        onPressItem={this._onPressItem}
        selected={!!this.state.selected.get(item.id)}
        title={item.title}
      />
    );
    
    render() {
      return (
        <FlatList
          data={this.props.data}
          extraData={this.state}
          keyExtractor={this._keyExtractor}
          renderItem={this._renderItem}
        />
      );
    }
    

    }

本組件實質是基於<VirtualizedList>組件的封裝,繼承了其所有 props(也包括所有<ScrollView>)的 props),但在本文檔中沒有列出。此外還有下面這些需要注意的事項:

  • 當某行滑出渲染區域之外後,其內部狀態將不會保留。請確保你在行組件以外的地方保留了數據。
  • 本組件繼承自PureComponent而非通常的Component,這意味著如果其props淺比較中是相等的,則不會重新渲染。所以請先檢查你的renderItem函數所依賴的props數據(包括data屬性以及可能用到的父組件的 state),如果是一個引用類型(Object 或者數組都是引用類型),則需要先修改其引用地址(比如先複製到一個新的 Object 或者數組中),然後再修改其值,否則界面很可能不會刷新。(譯註:這一段不瞭解的朋友建議先學習下js 中的基本類型和引用類型。)
  • 為了優化內存佔用同時保持滑動的流暢,列表內容會在屏幕外異步繪製。這意味著如果用戶滑動的速度超過渲染的速度,則會先看到空白的內容。這是為了優化不得不作出的妥協,你可以根據自己的需求調整相應的參數,而我們也在設法持續改進。
  • 默認情況下每行都需要提供一個不重複的 key 屬性。你也可以提供一個keyExtractor函數來生成 key。

本組件如果嵌套在其他同滾動方向的 FlatList 中,則不會繼承ScrollView 的 Props


文檔

Props

renderItem

renderItem({ item: Object, index: number, separators: { highlight: Function, unhighlight: Function, updateProps: Function(select: string, newProps: Object) } }) => ?React.Element

data中挨個取出數據並渲染到列表中。

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.

類型必填
function

示例:

<FlatList
  ItemSeparatorComponent={Platform.OS !== 'android' && ({highlighted}) => (
    <View style={[style.separator, highlighted && {marginLeft: 0}]} />
  )}
  data={[{title: 'Title Text', key: 'item1'}]}
  renderItem={({item, separators}) => (
    <TouchableHighlight
      onPress={() => this._onPress(item)}
      onShowUnderlay={separators.highlight}
      onHideUnderlay={separators.unhighlight}>
      <View style={{backgroundColor: 'white'}}>
        <Text>{item.title}</Text>
      </View>
    </TouchableHighlight>
  )}
/>

data

為了簡化起見,data 屬性目前只支持普通數組。如果需要使用其他特殊數據結構,例如 immutable 數組,請直接使用更底層的VirtualizedList組件。

類型必填
array

ItemSeparatorComponent

行與行之間的分隔線組件。不會出現在第一行之前和最後一行之後。 By default, highlighted and leadingItem props are provided. renderItem provides separators.highlight/unhighlight which will update the highlighted prop, but you can also add custom props with separators.updateProps.

類型必填
component

ListEmptyComponent

列表為空時渲染該組件。可以是 React Component, 也可以是一個 render 函數,或者渲染好的 element。

類型必填
component, function, element

ListFooterComponent

尾部組件。可以是 React Component, 也可以是一個 render 函數,或者渲染好的 element。

類型必填
component, function, element

ListHeaderComponent

頭部組件。可以是 React Component, 也可以是一個 render 函數,或者渲染好的 element。

類型必填
component, function, element

columnWrapperStyle

如果設置了多列布局(即將numColumns值設為大於 1 的整數),則可以額外指定此樣式作用在每行容器上。

類型必填
style object

extraData

如果有除data以外的數據用在列表中(不論是用在renderItem還是頭部或者尾部組件中),請在此屬性中指定。同時此數據在修改時也需要先修改其引用地址(比如先複製到一個新的 Object 或者數組中),然後再修改其值,否則界面很可能不會刷新。

類型必填
any

getItemLayout

(data, index) => {length: number, offset: number, index: number}

getItemLayout是一個可選的優化,用於避免動態測量內容尺寸的開銷,不過前提是你可以提前知道內容的高度。如果你的行高是固定的,getItemLayout用起來就既高效又簡單,類似下面這樣:

  getItemLayout={(data, index) => (
    {length: ITEM_HEIGHT, offset: ITEM_HEIGHT * index, index}
  )}

對於元素較多的列表(幾百行)來說,添加getItemLayout可以極大地提高性能。注意如果你指定了ItemSeparatorComponent,請把分隔線的尺寸也考慮到 offset 的計算之中。

類型必填
function

horizontal

設置為 true 則變為水平佈局模式。

類型必填
boolean

initialNumToRender

指定一開始渲染的元素數量,最好剛剛夠填滿一個屏幕,這樣保證了用最短的時間給用戶呈現可見的內容。注意這第一批次渲染的元素不會在滑動過程中被卸載,這樣是為了保證用戶執行返回頂部的操作時,不需要重新渲染首批元素。

類型必填
number

initialScrollIndex

開始時屏幕頂端的元素是列表中的第 initialScrollIndex個元素, 而不是第一個元素。如果設置了這個屬性,則第一批initialNumToRender範圍內的元素不會再保留在內存裡,而是直接立刻渲染位於 initialScrollIndex 位置的元素。需要先設置 getItemLayout 屬性。

類型必填
number

inverted

翻轉滾動方向。實質是將 scale 變換設置為-1。

類型必填
boolean

keyExtractor

(item: object, index: number) => string;

此函數用於為給定的 item 生成一個不重複的 key。Key 的作用是使 React 能夠區分同類元素的不同個體,以便在刷新時能夠確定其變化的位置,減少重新渲染的開銷。若不指定此函數,則默認抽取item.key作為 key 值。若item.key也不存在,則使用數組下標。

類型必填
function

numColumns

多列布局只能在非水平模式下使用,即必須是horizontal={false}。此時組件內元素會從左到右從上到下按 Z 字形排列,類似啟用了flexWrap的佈局。組件內元素必須是等高的——暫時還無法支持瀑布流佈局。

類型必填
number

onEndReached

(info: {distanceFromEnd: number}) => void

當列表被滾動到距離內容最底部不足onEndReachedThreshold的距離時調用。

類型必填
function

onEndReachedThreshold

決定當距離內容最底部還有多遠時觸發onEndReached回調。注意此參數是一個比值而非像素單位。比如,0.5 表示距離內容最底部的距離為當前列表可見長度的一半時觸發。

類型必填
number

onRefresh

() => void

如果設置了此選項,則會在列表頭部添加一個標準的RefreshControl控件,以便實現“下拉刷新”的功能。同時你需要正確設置refreshing屬性。

類型必填
function

onViewableItemsChanged

(info: {
    viewableItems: array,
    changed: array,
  }) => void

在可見行元素變化時調用。可見範圍和變化頻率等參數的配置請設置viewabilityConfig屬性。

類型必填
function

progressViewOffset

當需要在指定的偏移處顯示加載指示器的時候,就可以設置這個值。

類型必填平臺
numberAndroid

legacyImplementation

May not have full feature parity and is meant for debugging and performance comparison.

類型必填
boolean

refreshing

在等待加載新數據時將此屬性設為 true,列表就會顯示出一個正在加載的符號。

類型必填
boolean

removeClippedSubviews

對於大列表啟用本屬性可能可以提高性能。

注意:有些情況下會有 bug(比如內容無法顯示)。請謹慎使用。

類型必填
boolean

viewabilityConfig

請參考ViewabilityHelper.js的源碼來了解具體的配置。

類型必填
ViewabilityConfig

viewabilityConfig takes a type ViewabilityConfig an object with following properties

PropertyRequiredType
minimumViewTimeNonumber
viewAreaCoveragePercentThresholdNonumber
itemVisiblePercentThresholdNonumber
waitForInteractionNoboolean

At least one of the viewAreaCoveragePercentThreshold or itemVisiblePercentThreshold is required. This needs to be done in the constructor to avoid following error (ref):

  Error: Changing viewabilityConfig on the fly is not supported`
constructor (props) {
  super(props)

  this.viewabilityConfig = {
      waitForInteraction: true,
      viewAreaCoveragePercentThreshold: 95
  }
}
<FlatList
    viewabilityConfig={this.viewabilityConfig}
  ...

minimumViewTime

Minimum amount of time (in milliseconds) that an item must be physically viewable before the viewability callback will be fired. A high number means that scrolling through content without stopping will not mark the content as viewable.

viewAreaCoveragePercentThreshold

Percent of viewport that must be covered for a partially occluded item to count as “viewable”, 0-100. Fully visible items are always considered viewable. A value of 0 means that a single pixel in the viewport makes the item viewable, and a value of 100 means that an item must be either entirely visible or cover the entire viewport to count as viewable.

itemVisiblePercentThreshold

Similar to viewAreaPercentThreshold, but considers the percent of the item that is visible, rather than the fraction of the viewable area it covers.

waitForInteraction

Nothing is considered viewable until the user scrolls or recordInteraction is called after render.


viewabilityConfigCallbackPairs

List of ViewabilityConfig/onViewableItemsChanged pairs. A specific onViewableItemsChanged will be called when its corresponding ViewabilityConfig‘s conditions are met. 請參考ViewabilityHelper.js的源碼來了解具體的配置。

類型必填
array of ViewabilityConfigCallbackPair

方法

scrollToEnd()

scrollToEnd([params]);

滾動到底部。如果不設置getItemLayout屬性的話,可能會比較卡。

參數:

名稱類型必填說明
paramsobject看下面的說明

Valid params keys are:

  • ‘animated’ (boolean) – Whether the list should do an animation while scrolling. Defaults to true.

scrollToIndex()

scrollToIndex(params);

將位於指定位置的元素滾動到可視區的指定位置,當viewPosition 為 0 時將它滾動到屏幕頂部,為 1 時將它滾動到屏幕底部,為 0.5 時將它滾動到屏幕中央。

注意:如果不設置getItemLayout屬性的話,無法跳轉到當前渲染區域以外的位置。

參數:

名稱類型必填說明
paramsobject看下面的說明

Valid params keys are:

  • ‘animated’ (boolean) – Whether the list should do an animation while scrolling. Defaults to true.
  • ‘index’ (number) – The index to scroll to. Required.
  • ‘viewOffset’ (number) – A fixed number of pixels to offset the final target position. Required.
  • ‘viewPosition’ (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.

scrollToItem()

scrollToItem(params);

這個方法會順序遍歷元素。儘可能使用scrollToIndex代替。

注意:如果不設置getItemLayout屬性的話,無法跳轉到當前渲染區域以外的位置。

參數:

名稱類型必填說明
paramsobject看下面的說明

Valid params keys are:

  • ‘animated’ (boolean) – Whether the list should do an animation while scrolling. Defaults to true.
  • ‘item’ (object) – The item to scroll to. Required.
  • ‘viewPosition’ (number)

scrollToOffset()

scrollToOffset(params);

滾動列表到指定的偏移(以像素為單位),等同於ScrollViewscrollTo方法。

參數:

名稱類型必填說明
paramsobject看下面的說明

Valid params keys are:

  • ‘offset’ (number) – 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.

recordInteraction()

recordInteraction();

主動通知列表發生了一個事件,以使列表重新計算可視區域。比如說當waitForInteractions為 true 並且用戶沒有滾動列表時。一般在用戶點擊了列表項或發生了導航動作時調用。


flashScrollIndicators()

flashScrollIndicators();

短暫地顯示滾動指示器。

相關文章

Android抓包Charleshttp接口調試

小米殺不死的推送Android、java後端同時接入小米推送

設計模式單例模式

ReactNative性能優化組件PureComponent