Last active
July 22, 2021 17:24
-
-
Save bevacqua/b7c481cc0bfb56320ac96c7f8c7cb0b6 to your computer and use it in GitHub Desktop.
Defer secondary portions of the DOM using `requestAnimationFrame`
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
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 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
There is two things that trouble me:
createNonBlockingRenderLoop({ key: 'a', concurrencyLevel: 1 })
andcreateNonBlockingRenderLoop({ key: 'a', concurrencyLevel: 2 })
can have different behavior based on which has been called first.<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.