/
index.js
98 lines (82 loc) · 2.59 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
import React from 'react'
const changedArray = (a = [], b = []) =>
a.length !== b.length || a.some((item, index) => !Object.is(item, b[index]))
const initialState = {error: null}
class ErrorBoundary extends React.Component {
static getDerivedStateFromError(error) {
return {error}
}
state = initialState
updatedWithError = false
resetErrorBoundary = (...args) => {
this.props.onReset?.(...args)
this.reset()
}
reset() {
this.updatedWithError = false
this.setState(initialState)
}
componentDidCatch(error, info) {
this.props.onError?.(error, info)
}
componentDidUpdate(prevProps) {
const {error} = this.state
const {resetKeys} = this.props
// There's an edge case where if the thing that triggered the error
// happens to *also* be in the resetKeys array, we'd end up resetting
// the error boundary immediately. This would likely trigger a second
// error to be thrown.
// So we make sure that we don't check the resetKeys on the first call
// of cDU after the error is set
if (error !== null && !this.updatedWithError) {
this.updatedWithError = true
return
}
if (error !== null && changedArray(prevProps.resetKeys, resetKeys)) {
this.props.onResetKeysChange?.(prevProps.resetKeys, resetKeys)
this.reset()
}
}
render() {
const {error} = this.state
const {fallbackRender, FallbackComponent, fallback} = this.props
if (error !== null) {
const props = {
error,
resetErrorBoundary: this.resetErrorBoundary,
}
if (React.isValidElement(fallback)) {
return fallback
} else if (typeof fallbackRender === 'function') {
return fallbackRender(props)
} else if (FallbackComponent) {
return <FallbackComponent {...props} />
} else {
throw new Error(
'react-error-boundary requires either a fallback, fallbackRender, or FallbackComponent prop',
)
}
}
return this.props.children
}
}
function withErrorBoundary(Component, errorBoundaryProps) {
function Wrapped(props) {
return (
<ErrorBoundary {...errorBoundaryProps}>
<Component {...props} />
</ErrorBoundary>
)
}
// Format for display in DevTools
const name = Component.displayName || Component.name || 'Unknown'
Wrapped.displayName = `withErrorBoundary(${name})`
return Wrapped
}
function useErrorHandler(givenError) {
const [error, setError] = React.useState(null)
if (givenError) throw givenError
if (error) throw error
return setError
}
export {ErrorBoundary, withErrorBoundary, useErrorHandler}