Skip to content

Instantly share code, notes, and snippets.

@drcmda
Created September 10, 2018 14:52
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save drcmda/4d56531fde15b22561c8a56e8ae7a682 to your computer and use it in GitHub Desktop.
Save drcmda/4d56531fde15b22561c8a56e8ae7a682 to your computer and use it in GitHub Desktop.
React 16 using throw and error bounds
import React from 'react'
import ReactDOM from 'react-dom'
import { Spring, animated, config } from 'react-spring'
let GUID = 0
let CURRENT_TARGET = undefined
let CURRENT_COUNT = 0
let CURRENT_THROWS = 0
let ADOPTS = {}
const adopt = ref => {
if (CURRENT_TARGET === undefined) throw Promise.resolve(0)
let target = CURRENT_TARGET
let id = CURRENT_COUNT++
if (!ADOPTS[target][id]) {
ADOPTS[target][id] = {}
CURRENT_THROWS++
throw Promise.resolve({
ref,
cb: value => (ADOPTS[target][id].result = value),
})
} else return ADOPTS[target][id].result
}
const wrap = Wrapped =>
class SuspenseWrapper extends React.PureComponent {
constructor() {
super()
const id = GUID++
this.state = { refs: [], id }
ADOPTS[id] = {}
}
componentWillUnmount() {
delete ADOPTS[this.state.id]
}
static getDerivedStateFromProps(props, state) {
return { ...state, refs: Object.keys(ADOPTS[state.id]).length ? state.refs : [] }
}
render() {
const { refs, id } = this.state
CURRENT_THROWS = 0
CURRENT_COUNT = 0
CURRENT_TARGET = id
return refs.reduceRight(
(inner, { ref, cb }) => (...args) => (
<ref.type {...ref.props} children={value => inner(cb, value)} />
),
(cb, value) => {
cb && cb(value)
return <Wrapped {...this.props} />
}
)()
}
async componentDidCatch(data) {
if (!data.then) return
data = await data
if (data === 0) return this.forceUpdate()
this.setState(state => ({ refs: [...state.refs, data] }))
}
componentDidUpdate() {
if (CURRENT_THROWS === 0) {
CURRENT_TARGET = undefined
ADOPTS[this.state.id] = {}
}
}
}
const { Provider, Consumer } = React.createContext()
const Add = ({ number, children }) => children(number + 1)
const springConfig = {
from: {
willChange: 'transform',
transform: 'perspective(600px) rotateY(90deg) scale(0)',
},
to: { transform: 'perspective(600px) rotateY(0deg) scale(1)' },
config: { tension: 2, friction: 1 },
}
@wrap
class Count extends React.Component {
render() {
const start = adopt(<Consumer />) // 2
const result = adopt(<Add number={start} />) // 3
const styles = adopt(<Spring native {...springConfig} />)
return <animated.div style={styles}>{result}</animated.div>
}
}
class App extends React.Component {
state = { number: 0 }
componentDidMount() {
setTimeout(
() => this.setState(state => ({ number: state.number + 1 })),
2000
)
}
render() {
return (
<Provider value={this.state.number}>
<Count />
</Provider>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'))
@drcmda
Copy link
Author

drcmda commented Sep 10, 2018

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment