Skip to content

Instantly share code, notes, and snippets.

@sylvesteraswin
Forked from threepointone/infinite.js
Created January 6, 2017 19:41
Show Gist options
  • Save sylvesteraswin/84d5a7ace192ba875ba0d04e94009dac to your computer and use it in GitHub Desktop.
Save sylvesteraswin/84d5a7ace192ba875ba0d04e94009dac to your computer and use it in GitHub Desktop.
infinite scrolling pattern with react fiber (featuring intersection observers)
// inifinite scrolling of content without extra wrappers
const { render, findDOMNode } = ReactDOMFiber
class App extends React.Component {
render() {
// wrap the root element with an Intersection Observer, exposing .observe for children
return <Intersection>
<div style={{ height: 200, overflow: 'auto' }}>
<Page offset={0} count={10} />
</div>
</Intersection>
}
}
class Page extends React.Component {
static contextTypes = {
observe: React.PropTypes.func
}
state = {
next: false
}
componentDidMount() {
// load more content when this element comes into view
this.context.observe(this.loadMore, () => this.setState({ next: true }))
}
render() {
let { count, offset } = this.props
return [
times(count, i =>
<div>
article {this.props.offset + i}
</div>),
this.state.next ?
<Page offset={offset + count} count={count}/> :
<div ref={x => this.loadMore = x || this.loadMore }> load more ...</div>
]
}
}
class Intersection extends React.Component {
static childContextTypes = {
observe: React.PropTypes.func
}
getChildContext(){
return {
observe: this.observe
}
}
elements = new Map()
elementBuffer = []
observe = (element, callback) => {
this.elements.set(element, callback)
// this funny bit to handle react's lifecycle order
if(!this.observer){
this.elementBuffer.push(element)
}
else {
this.observer.observe(element)
}
}
onIntersect = (entries, observer) => {
entries.forEach(entry => this.elements.get(entry.target)(entry))
}
componentDidMount(){
this.observer = new IntersectionObserver(this.onIntersect, {
root: findDOMNode(this),
rootMargin: '0px',
threshold: 0.25
})
this.elementBuffer.forEach(element => this.observer.observe(element))
this.elementBuffer = []
}
render() {
return this.props.children
}
}
function times(n, fn){
let arr = []
for(let i=0; i< n; i++){
arr.push(fn(i))
}
return arr
}
render(<App/>, window.app)
// homework -
// reclaim memory by removing dom nodes from the top without jitter
// go both ways; start from the middle and scroll up
// cleanup element handlers after loading
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment