The simplest way to make a pull-to-refresh and infinite tableview with ReactJS

Calvin Chan
3 min readJun 2, 2016

--

Pull-to-refresh and pull-to-load-more are extremely common to show data in web, there are so many libraries to make it in React, but there are some problems.šŸ˜©

  • Most of them are difficult to subclass, you need to use their component to render the cells. It is quite troublesome if you want to add more actions in you cells.
  • The refresh libraries and infinite ones are usually separate, you have to integrate those libraries and change them to make them same style for your web.
  • Some of them are quite bulky or you even have to include extra dependencies.

As a react beginner, the first question come up in my mind isā€¦are those necessary?šŸ¤”

OKā€¦then what are necessary in such tableview?

  1. Subscribe the scroll to top & bottom events
  2. Refresh/load more content
  3. Insert loading indicators
  4. Dismiss loading indicators once new data received

Well..how about make it on my own???

Scroll to top & bottom events

Thanks Google, there are some DOM properties, scrollTop, clientHeight and scrollHeight, should be enough to detect the scroll-to-up & scroll-to-bottom events, letā€™s use it in the onScroll event.

var scrollviewOffsetY = dom.scrollTop
var scrollviewFrameHeight = dom.clientHeight
var scrollviewContentHeight = dom.scrollHeight
var sum = scrollviewOffsetY+scrollviewFrameHeight
if (sum <= scrollviewFrameHeight) {
// the top reached
} else if (sum >= scrollviewContentHeight) {
// the bottom reached
}

Refresh & load-more data

Thanks Facebook, it is easy to be done in React. The scrollview just needs to tell its parent that it needs new data via props.

In tableview component

this.props.onScrollToTop

In its parent

// inside render()<ExampleTableView
dataSource={this.state.data}
onScrollToTop={this.handleScrollToTop}
/>
// custom methodhandleScrollToTop() {
// refresh
myRefreshMethod(function() {
this.setState({data: data})
}.bind(this))
}

Actually, it is basically done !!! It will refresh/load-more data when I pull up/down. BUT my users wonā€™t know it becauseā€¦there isnā€™t any indicators showing to my users that it is loadingā€¦

Insert loading indicators

There are at least two ways to do it, insert the indicators via DOM API or render the indicators via ReactDOM.

via DOM API

var refreshIndicator = document.createElement(ā€œdivā€)
refreshIndicator.className = "spinner"
var tableview = document.getElementById("myTableview");
tableview.insertBefore(refreshIndicator, tableview.firstChild)

via ReactDOM

render() {return (
<div className=ā€myTableviewā€ onScroll={this.viewDidScroll}>
{this.refreshIndicator()}
{cells}
</div>
)
}
refreshIndicator() {
if (this.state.isRefreshing) {
return (
<div className=ā€spinnerā€>šŸƒā€¦</div>
)
}
return
}

Dismiss loading indicators once new data received

Well, if you inserted elements via DOM API, letā€™s try using callback in parent to tell the tableview to dismiss the loading indicators.

in parent

handleScrollToTop(refreshCompleted) {
// refresh
myRefreshMethod(function() {
refreshCompleted()
this.setState({data: data})
}.bind(this))
}

in tableview

this.props.onScrollToTop(function() {

// dismiss my spinner here
var tableView = document.getElementById(tableViewIdName)
var firstChild = tableView.firstChild
if (firstChild.className.indexOf("spinner") > -1) {
tableView.removeChild(firstChild)
}
}.bind(this))

If you render your indicators via ReactDOM, letā€™s declare a state ā€˜isRefreshingā€™, assign the states once data received and React will automagically do it for you.

refreshIndicator() {
if (this.state.isRefreshing) {
return (
<div className=ā€spinnerā€>šŸƒā€¦</div>
)
}
return
}

Whatā€™s next?

NOTHING !!! IT IS ALL DONE !!!

Source code?

If you wanna know more about the implementation, please checkout my repo.

You can also try out the ready-to-use package to enable pull-to-refresh and infinite in your own scrollview in a minute.šŸ™‚

--

--