// 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
} } 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 =>
article {this.props.offset + i}
), this.state.next ? :
this.loadMore = x || this.loadMore }> load more ...
] } } 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(, 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