Created
July 13, 2017 16:50
-
-
Save 8lane/2cb9f5f30dc663b9762fb1e6105d07f6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
class InfiniteScroll extends React.Component { | |
constructor(props) { | |
super(props); | |
this.state = { | |
scrollElement: null, | |
waypointClassName: 'infinite-scroll__waypoint', | |
} | |
this.scrollListener = this.scrollListener.bind(this); | |
} | |
componentDidMount() { | |
this.attachScrollListener(); | |
} | |
componentWillUnmount() { | |
this.detachScrollListener(); | |
} | |
attachScrollListener() { | |
const scrollElement = this.getScrollParent(this.scrollComponent, this.props.scrollElementName); | |
this.setState({ scrollElement }); | |
scrollElement.addEventListener('scroll', this.scrollListener); | |
scrollElement.addEventListener('resize', this.scrollListener); | |
} | |
detachScrollListener() { | |
const { scrollElement } = this.state; | |
scrollElement.removeEventListener('scroll', this.scrollListener); | |
scrollElement.removeEventListener('resize', this.scrollListener); | |
} | |
getScrollParent(el, className) { | |
while ((el = el.parentElement) && !el.classList.contains(className)); | |
return el; | |
} | |
scrollListener() { | |
const { scrollElement } = this.state; | |
const { | |
currentPage, | |
clickToUpdate, | |
hasMore, | |
loading, | |
onUpdate, | |
} = this.props; | |
const isVisible = this.isWaypointVisible(scrollElement, this.waypointNode); | |
if (!loading && !clickToUpdate && hasMore && isVisible) { | |
onUpdate(currentPage + 1); | |
} | |
} | |
isWaypointVisible(container, waypoint) { | |
const containerRect = container.getBoundingClientRect(); | |
const waypointRect = waypoint.getBoundingClientRect(); | |
return waypointRect.top < (container.offsetHeight + containerRect.top); | |
} | |
render() { | |
const { | |
children, | |
currentPage, | |
loading, | |
className, | |
clickToUpdate, | |
clickToUpdateBtn, | |
onUpdate | |
} = this.props; | |
const { waypointClassName } = this.state; | |
return ( | |
<div ref={node => { this.scrollComponent = node }} className={`infinite-scroll ${className}`}> | |
{children} | |
{loading && children.length > 0 && <Spinner size={40} className="infinite-scroll__loader" />} | |
{children.length > 0 && !clickToUpdate && | |
<span ref={node => { this.waypointNode = node }} className={waypointClassName} /> | |
} | |
{children.length > 0 && clickToUpdate && | |
<button | |
ref={node => { this.waypointNode = node }} | |
className={waypointClassName} | |
disabled={loading} | |
onClick={() => onUpdate(currentPage + 1)}> | |
{this.props.clickToUpdateBtn} | |
</button> | |
} | |
</div> | |
); | |
} | |
} | |
InfiniteScroll.defaultProps = { | |
className: '', | |
children: [], | |
hasMore: true, | |
currentPage: 1, | |
clickToUpdate: false, | |
clickToUpdateBtn: 'Load more...', | |
} | |
InfiniteScroll.propTypes = { | |
children: React.PropTypes.array, | |
hasMore: React.PropTypes.bool, | |
scrollElement: React.PropTypes.string, | |
currentPage: React.PropTypes.number, | |
className: React.PropTypes.string, | |
clickToUpdate: React.PropTypes.bool, | |
clickToUpdateBtn: React.PropTypes.string, | |
loading: React.PropTypes.bool.isRequired, | |
onUpdate: React.PropTypes.func.isRequired, | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment