Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Defer secondary portions of the DOM using `requestAnimationFrame`
import { Component, PropTypes } from 'react'
import { noop } from 'lodash'
import raf from 'raf'
const cache = new Map()
export function createNonBlockingRenderLoop({ key, concurrencyLevel = 1 }) {
if (cache.has(key)) {
return cache.get(key)
}
let slots = 0
const isBusy = () => slots >= concurrencyLevel
const takeSlot = () => slots++
const releaseSlot = () => slots--
class NonBlockingRenderLoop extends Component {
state = {
wait: true
}
componentDidMount() {
this.enqueue()
}
componentWillUnmount() {
this.stop()
}
render() {
return this.state.wait ? null : this.props.children
}
enqueue() {
if (isBusy()) {
this.defer(() => this.enqueue())
} else {
takeSlot()
this.defer(() => this.dequeue())
}
}
dequeue() {
this.setState({ wait: false })
releaseSlot()
}
defer(fn) {
raf(() => {
if (this.defer !== noop) {
fn()
}
})
}
stop() {
this.defer = noop
if (this.state.wait) {
releaseSlot()
}
}
}
NonBlockingRenderLoop.propTypes = {
children: PropTypes.element.isRequired
}
cache.set(key, NonBlockingRenderLoop)
return NonBlockingRenderLoop
}
@EECOLOR

This comment has been minimized.

Copy link

EECOLOR commented Dec 14, 2016

There is two things that trouble me:

  1. createNonBlockingRenderLoop({ key: 'a', concurrencyLevel: 1 }) and createNonBlockingRenderLoop({ key: 'a', concurrencyLevel: 2 }) can have different behavior based on which has been called first.
  2. Instead of just using <NonBlockingRenderLoop .../> I need to call the function.

1 Could be solved in a variety of ways, the simplest is issuing a warning.
2 Could be solved by implementing the lock mechanism in a static property of the component.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.