Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
React Native pull down to refresh ListView
// for an updated version see https://github.com/jsdf/react-native-refreshable-listview
var React = require('react-native')
var {
ListView,
ActivityIndicatorIOS,
StyleSheet,
View,
Text,
} = React
// must be less than ~50px due to ScrollView bug (event only fires once)
// https://github.com/facebook/react-native/pull/452
// TODO: expose as a prop when onScroll works properly
var PULLDOWN_DISTANCE = 40 // pixels
var RefreshableListView = React.createClass({
getInitialState() {
return {
reloading: false,
}
},
handleScroll(e) {
if (e.nativeEvent.contentOffset.y < -PULLDOWN_DISTANCE) {
this.reloadData()
}
this.props.onScroll && this.props.onScroll(e)
},
reloadData() {
if (this.willReload || this.state.reloading) return
this.willReload = true
Promise.all([
this.props.loadData(),
new Promise((resolve) => this.setState({reloading: true}, resolve)),
new Promise((resolve) => setTimeout(resolve, 300)),
]).then(([data]) => {
this.willReload = false
this.setState({reloading: false})
})
},
renderHeader() {
if (this.state.reloading) {
return (
<View style={[styles.container, styles.wrapper]}>
<View style={[styles.container, styles.loading]}>
<Text>{this.props.refreshDescription}</Text>
<ActivityIndicatorIOS />
</View>
</View>
)
} else {
return null
}
},
render() {
return (
<ListView
{...this.props}
onScroll={this.handleScroll}
renderHeader={this.renderHeader}
/>
)
}
})
var styles = StyleSheet.create({
wrapper: {
height: 60,
marginTop: 10,
},
container: {
flex: 1,
justifyContent: 'space-around',
alignItems: 'center',
backgroundColor: 'white',
},
loading: {
height: 60,
},
})
RefreshableListView.DataSource = ListView.DataSource
module.exports = RefreshableListView
@jsdf
Copy link
Author

jsdf commented Mar 31, 2015

usage example

var React = require('react-native')
var {Text, View, ListView} = React

var ArticleStore = require('../stores/ArticleStore')
var StoreWatchMixin = require('./StoreWatchMixin')
var ArticleView = require('./ArticleView')
var RefreshableListView = require('./RefreshableListView')

var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1.id !== r2.id})

var ArticlesScreen = React.createClass({
  mixins: [StoreWatchMixin],
  getInitialState() {
    return {dataSource: ds.cloneWithRows(ArticleStore.all())}
  },
  getStoreWatches() {
    this.watchStore(ArticleStore, () => {
      this.setState({dataSource: ds.cloneWithRows(ArticleStore.all())})
    })
  },
  reloadArticles() {
    return ArticleStore.reload() // returns a promise of reload completion
  },
  renderArticle(article) {
    return <ArticleView article={article} />
  },
  render() {
    // important props
    // - loadData: a function returning a promise, invoked upon pulldown. 
    //   spinner will show until the promise resolves
    // - refreshDescription: text/content to show alongside spinner
    return (
      <RefreshableListView
        dataSource={this.state.dataSource}
        renderRow={this.renderArticle}
        loadData={this.reloadArticles}
        refreshDescription="Refreshing articles"
      />
    )
  }
})

@jsdf
Copy link
Author

jsdf commented Mar 31, 2015

In action (from ReactNativeHackerNews):

React Native Hacker News

@jsdf
Copy link
Author

jsdf commented Mar 31, 2015

Installable version: react-native-refreshable-listview

@thanhtungdp
Copy link

thanhtungdp commented Sep 14, 2016

Hi, it doesn't work on android. ^_^

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment