Learn to encapsulate and reduce unnecessary rework.

Before packaging, design the functionality and API that the component should have, and then build on that.

1. Design of the List component

The API should be similar to the official website as much as possible to reduce the learning cost. Provide a single API function, can not one API multiple uses, reduce the chance of pit. There should be a uniform specification, specifying components that can only have one output for the same behavior.

1.1 List Specification and Description

This List contains both FlatList and SectionList.

  • A normal list when data is passed, and a grouping list when sections are passed
  • Pull-up loading and pull-down refreshing use React-native Spinkit and are not customizable
  • You must specify the height of each item, the height of item, the height of section can not be specified, do not specify the height of 0
  • Refresh and loading cannot be manually enabled
  • You can end the refresh with a callback in onRefresh, or you can end the refresh with a ref, and the load is the same

1.2 the API definition

api Type specification API details
data array Data source, one-dimensional data
sections array When it’s a two-dimensional array of data
renderItem ()=> React.ReactElement Render each item
ListEmptyComponent ()=> React.ReactElement A component that is rendered when the data is empty
ListFooterComponent ()=> React.ReactElement The tail components
ListHeaderComponent ()=> React.ReactElement The head component
onRefresh function Function that is called when the flush occurs, providing a callback, and executing to indicate the end of the flush
onEndReached function When the function called at the bottom is reached, a callback is provided, and execution represents the end of the pull-up
itemHeight (index)=>number The height of each of these terms
sectionHeight (index)=>number The height of each group’s head
renderSection ()=> React.ReactElement Render the head of each group
loadingCompleted boolean Whether the data is complete when the pull-up is loaded
style ViewStyle Overall style
itemStyle ViewStyle The style of each item
sectionStyle ViewStyle The style of each set of heads

Methods provided

api The function definitions Using the details
scrollToEnd list.scrollToEnd(); Jump to the bottom
scrollToStart list.scrollToStart(); Jump to the top
scrollToIndex list.scrollToIndex(index) Jumps to the specified item
scrollToSectionIndex list.scrollToSectionIndex(index) Jumps to the specified section header
endRefresh endRefresh() The end of the refresh
endEndReached endEndReached() The pull-up loading is complete

Implemented apis and unimplemented apis

  • data
  • sections
  • renderItem
  • ListEmptyComponent
  • ListFooterComponent
  • ListHeaderComponent
  • onRefresh
  • onEndReached
  • itemHeight
  • sectionHeight
  • renderSection
  • loadingCompleted
  • style
  • itemStyle
  • sectionStyle
  • scrollToEnd
  • scrollToStart
  • scrollToIndex
  • scrollToSectionIndex
  • endRefresh
  • endEndReached

Three, specific code

import React, { forwardRef, useRef, useImperativeHandle } from 'react'
import { LargeList, RefreshHeader, SpinKit } from '.. /.. /.. /.. /modules'
import { View, StyleSheet } from 'react-native'
import { LoadingFooter } from 'react-native-spring-scrollview'


const __Refresh = ((refreshStyle) = > {
  let refreshInstance = null
  return () = > {
    if (refreshInstance === null) {
      refreshInstance = class extends RefreshHeader {
        render() {
          const { size = 20, type = "Circle", color = "red" } = refreshStyle || {}
          return (
            <View style={{ justifyContent: 'center', alignItems: 'center' }}>
              <SpinKit isVisible={true} size={size} type={type} color={color} />
            </View>)}}}return refreshInstance
  }
})()


const __EndReached = ((endReachedStyle) = > {
  let endReachedInstance = null
  return () = > {
    if (endReachedInstance === null) {
      endReachedInstance = class extends LoadingFooter {
        render() {
          const { size = 20, type = "Circle", color = "red" } = endReachedStyle || {}
          return (
            <View style={{ justifyContent: 'center', alignItems: 'center' }}>
              <SpinKit isVisible={true} size={size} type={type} color={color} />
            </View>)}}}return endReachedInstance
  }
})()

const getData = (data, sections) = > {
  if(! data) data = []else if(! sections) sections = []if (!Array.isArray(data)) throw new Error("Instead of passing in an array, you pass in:".typeof data)

  if (data) {
    return [{ items: data }]
  } else {
    return sections || []
  }
}
const RenderItem = (section, index, data, renderItem, itemStyle, itemHeight) = > {
  return (<View style={StyleSheet.flatten([itemStyle, { height: itemHeight({ section.index }), overflow: 'hidden' }])}>
    {renderItem({ item: data[section].items[index], index })}
  </View>)}const RenderSection = (section, data, renderSection, sectionStyle, sectionHeight) = > {
  return (<View style={StyleSheet.flatten([sectionStyle, { height: sectionHeight(section), overflow: 'hidden' }])}>
    {renderSection({ section: data[section], index: section })}
  </View>)}const List = forwardRef(({ data, renderItem, ListFooterComponent, ListHeaderComponent, onRefresh, onEndReached, itemHeight, sectionHeight, renderSection, loadingCompleted, style = {}, refreshStyle = {}, endReachedStyle = {} }, ref) = > {
  const listRef = useRef(null)
  useImperativeHandle(ref, () = > {
    return {
      ...listRef.current || {},
      scrollToStart: () = > {
        console.log("By going to scrollToStart.")
        listRef.current.scrollToIndexPath({ section: 0.row: 0})},scrollToEnd: () = > {
        let row = data[data.length - 1].items.length - 1
        listRef.current.scrollToIndexPath({ section: data.length - 1, row })
      },
      scrollToIndex: (index) = > {
        if (data.length > 1) {
          console.warn("This API works for regular lists, use scrollToSectionIndex for grouped lists.")}if (index >= data[0].length) {
          index = data[0].length - 1
        }
        listRef.current.scrollToIndexPath({ section: 0.row: index })
      },
      scrollToSectionIndex: (section) = > {
        if (section >= data.length) {
          section = data.length - 1
        }
        listRef.current.scrollToIndexPath({ section: section, row: 0}}}})const endRefresh = () = > {
    if (listRef && listRef.current) {
      listRef.current.endRefresh()
    }
  }
  const _onRefresh = () = > {
    onRefresh && onRefresh(endRefresh)
  }
  const endLoading = () = > {
    if (listRef && listRef.current) {
      listRef.current.endLoading
    }
  }
  const _onEndReached = () = > {
    onEndReached && onEndReached(endLoading)
  }

  if(! itemHeight) {throw new Error(The height of each item must be specified.)}else if (typeofitemHeight ! = ='function') {
    throw new Error("ItemHeight requires a function type, not:".typeof (itemHeight))
  }
  return (
    <LargeList
      data={data}
      ref={listRef}
      style={style}
      {. onRefresh && { refreshHeader: __Refresh(refreshStyle), onRefresh: _onRefresh }}
      {. onEndReached && { loadingFooter: __EndReached(endReachedStyle), onEndReached: _onEndReached }}
      heightForIndexPath={itemHeight}
      renderHeader={ListHeaderComponent}
      renderFooter={ListFooterComponent}
      renderSection={renderSection}
      allLoaded={loadingCompleted}
      heightForSection={sectionHeight}
      renderIndexPath={renderItem} />)})const SwitchEmptyOrList = forwardRef(({
  data, //Data, one-dimensional array sections, renderItem, itemStyle, renderSection, sectionStyle, sectionHeight = () =>0,
  itemHeight,
  ListEmptyComponent = () => null. props }, ref) = > {
  const _data = getData(data, sections)
  if (_data.length === 0) {
    return ListEmptyComponent()
  } else {
    return (<List
      data={_data}
      ref={ref}
      renderItem={({ section.row}) = >RenderItem(section, row, _data, renderItem, itemStyle, itemHeight)} {... _data.length > 1 && { renderSection, sectionHeight: (section) => RenderSection(section, _data, renderSection, sectionStyle, sectionHeight) }} itemHeight={itemHeight} {... props} />)}})export default SwitchEmptyOrList
Copy the code

Four, simple use

function TestList() {
  const _renderItem = ({item, index}) = > {
    return (
       <Text key={index}>{item}</Text>)}return (
    <List 
       data={[1, 2.3.4.5.6]}
       itemHeight={()= > 50}
       renderItem={_renderItem}
     />)}Copy the code