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 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