├── README.md ├── RefreshableListView.js ├── TopButton.js ├── demo.gif ├── img ├── va-top.png ├── va-top@2x.png └── va-top@3x.png └── package.json /README.md: -------------------------------------------------------------------------------- 1 | # react-native-sk-refreshable-listview 2 | 3 | ##What is it 4 | 5 | react-native-sk-refreshable-listview is a component wraps ListView, supports: 1 pull down to refresh 2 pull up to load more 3 scroll to top 4 scroll to bottom 6 | 7 | ##How to use it 8 | 9 | 1. `npm install react-native-sk-refreshable-listview@latest --save` 10 | 11 | 2. Write this in index.ios.js / index.android.js 12 | 13 | ```javascript 14 | 15 | 'use strict'; 16 | import React, { 17 | AppRegistry, 18 | StyleSheet, 19 | Text, 20 | View, 21 | Dimensions, 22 | } from 'react-native'; 23 | 24 | 25 | var RefreshableListView = require('react-native-sk-refreshable-listview'); 26 | var {width, height} = Dimensions.get('window'); 27 | var dataUrl = 'https://raw.githubusercontent.com/shigebeyond/react-native-sk-refreshable-listview/master/test.json'; 28 | var data = [ 29 | {id: 1, text: 'row 1'}, 30 | {id: 2, text: 'row 2'}, 31 | {id: 3, text: 'row 3'}, 32 | {id: 4, text: 'row 4'}, 33 | {id: 5, text: 'row 5'}, 34 | {id: 6, text: 'row 6'}, 35 | {id: 7, text: 'row 7'}, 36 | {id: 8, text: 'row 8'}, 37 | {id: 9, text: 'row 9'}, 38 | {id: 10, text: 'row 10'}, 39 | ]; 40 | 41 | // simulate fetch() 42 | function skFetch(url){ 43 | return new Promise((resolve, reject) => { 44 | setTimeout(() => { 45 | resolve(data); 46 | }, 1000) 47 | }); 48 | } 49 | 50 | var test = React.createClass({ 51 | getInitialState() { 52 | return { 53 | dataSource : new RefreshableListView.DataSource({rowHasChanged : (row1, row2) => row1 !== row2}), 54 | } 55 | }, 56 | componentDidMount() { 57 | this.fetchData(true); 58 | }, 59 | 60 | /** 61 | * load data 62 | * @param bool refresh: whether to refresh data, or load more data 63 | * @return Promise 64 | */ 65 | fetchData(refresh){ 66 | if(refresh){ 67 | this.nextPage = 1; 68 | } 69 | // get the data url of next page 70 | var nextDataUrl = dataUrl + '?page=' + this.nextPage; 71 | // I use skFetch() to simulate fetch() 72 | return skFetch(nextDataUrl) 73 | .then((result) => { 74 | var newRows; 75 | if(refresh){ // set rows of dataSource 76 | newRows = result; 77 | }else{// add new rows into dataSource 78 | newRows = this.getRows().concat(result); 79 | } 80 | var newDataSource = this.state.dataSource.cloneWithRows(newRows); 81 | this.setState({ 82 | dataSource: newDataSource, 83 | }); 84 | this.nextPage++; 85 | }).catch((e)=>{ 86 | console.log(e); 87 | }); 88 | //.done(); 89 | }, 90 | 91 | // get all rows of dataSource 92 | getRows() { 93 | var result = this.state.dataSource && this.state.dataSource._dataBlob && this.state.dataSource._dataBlob.s1; 94 | return result ? result : []; 95 | }, 96 | 97 | // whether no row in dataSource 98 | isEmpty(){ 99 | return this.getRows().length == 0; 100 | }, 101 | 102 | renderRow(row) { 103 | return ( 104 | 105 | {row.text} 106 | 107 | ); 108 | }, 109 | 110 | render() { 111 | if(this.isEmpty()){ 112 | return ( 113 | 114 | {'Please pull down to fresh data, \n pull up to load more data'} 115 | 116 | ) 117 | } 118 | return ( 119 | this.fetchData(true)} // callback to refresh data (load first page of data), which should return a Promise, I use this promise to tell when to stop loading (render loading view). 123 | onLoadMore={() => this.fetchData(false)} // callback to load more data (load next page of data), which should return a Promise, I use this promise to tell when to stop loading (render loading view) 124 | showLoadMore={true} 125 | renderRow={this.renderRow} 126 | /> 127 | ); 128 | } 129 | }); 130 | 131 | var styles = { 132 | emptyBox: { 133 | flex: 1, 134 | justifyContent: 'center', 135 | alignItems: 'center' 136 | }, 137 | emptyTxt: { 138 | fontSize: 23, 139 | fontWeight: 'bold' 140 | }, 141 | container: { 142 | flex: 1, 143 | backgroundColor: '#FFF', 144 | }, 145 | row: { 146 | padding: 10, 147 | height: height / 10, 148 | backgroundColor: 'yellow', 149 | borderBottomColor: 'grey', 150 | borderBottomWidth: 2, 151 | }, 152 | }; 153 | 154 | AppRegistry.registerComponent('test', () => test); 155 | 156 | ``` 157 | ![](https://raw.githubusercontent.com/shigebeyond/react-native-sk-refreshable-listview/master/demo.gif) 158 | 159 | ##Properties 160 | 161 | Any [View property](http://facebook.github.io/react-native/docs/view.html) and the following: 162 | 163 | | Prop | Description | Default | 164 | |---|---|---| 165 | |**`onRefresh`**|callback to refresh data (load first page of data), which should return a Promise, I use this promise to tell when to stop loading (render loading view). |*None*| 166 | |**`onLoadMore`**|callback to load more data (load next page of data), which should return a Promise, I use this promise to tell when to stop loading (render loading view). |*None*| 167 | 168 | ##Methods 169 | 170 | | Method | Description | Params | 171 | |---|---|---| 172 | |**`scrollToTop`**|scroll to top. |*None*| 173 | |**`scrollToBottom`**|scroll to bottom. |*None*| 174 | -------------------------------------------------------------------------------- /RefreshableListView.js: -------------------------------------------------------------------------------- 1 | var React = require('react-native') 2 | var { 3 | ListView, 4 | StyleSheet, 5 | View, 6 | Text, 7 | Dimensions, 8 | PropTypes, 9 | RefreshControl 10 | } = React; 11 | 12 | var TopButton = require('./TopButton'), 13 | Loader = require('react-native-sk-loader'), // 加载器 14 | {width} = Dimensions.get('window'); 15 | 16 | var RefreshableListView = React.createClass({ 17 | statics: { 18 | DataSource: ListView.DataSource, 19 | }, 20 | 21 | propTypes: { 22 | onRefresh: PropTypes.func.isRequired, // 刷新数据的回调 23 | onLoadMore: PropTypes.func.isRequired, // 加载更多数据的回调 24 | }, 25 | 26 | listHeight: 0, // listview(scrollview)高度 27 | contentHeight: 0, // contentView高度 28 | 29 | getDefaultProps() { 30 | return { 31 | showLoadMore: false, // 是否显示加载更多 32 | hasTopButton: false, // 是否显示回到顶部的按钮 33 | } 34 | }, 35 | 36 | getInitialState() { 37 | return { 38 | isRefreshing: false, // 是否正在刷新 39 | isLoadingMore: false, // 是否正在加载更多 40 | } 41 | }, 42 | 43 | // 渲染脚部 44 | renderFooter: function() { 45 | // 没有更多/下一页数据 46 | if (!this.props.showLoadMore) { 47 | return null; 48 | } 49 | 50 | // 自定义渲染 51 | if (this.props.renderFooter) { 52 | return this.props.renderFooter(this.state.isLoadingMore) 53 | } 54 | 55 | // 默认渲染 56 | return (