Skip to content

Commit 950ef56

Browse files
author
Joschua Schneider
committed
Implements hook logic in typescript
1 parent 857e504 commit 950ef56

File tree

1 file changed

+116
-0
lines changed

1 file changed

+116
-0
lines changed

src/use-error-boundary.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,116 @@
1+
import React, { useRef, useReducer } from "react"
2+
3+
import {
4+
createErrorBoundary,
5+
UseErrorBoundaryWrapper,
6+
} from "./create-error-boundary"
7+
8+
export interface ErrorState {
9+
didCatch: boolean
10+
error: any | null
11+
}
12+
13+
export interface UseErrorBoundaryState extends ErrorState {
14+
ErrorBoundary: UseErrorBoundaryWrapper
15+
}
16+
17+
interface StateAction {
18+
type: "catch"
19+
error?: any | null
20+
}
21+
22+
/**
23+
* useErrorBoundary hook options.
24+
*/
25+
export interface UseErrorBoundaryOptions {
26+
/**
27+
* Gets called when the ErrorBoundary catches an error.
28+
*
29+
* You can use this for logging or reporting errors.
30+
*/
31+
onDidCatch?: (error: any, errorInfo: any) => void
32+
}
33+
34+
/**
35+
* useErrorBoundary
36+
* React hook to use an ErrorBoundary in your component and keep track of the
37+
* error state of that boundary.
38+
*
39+
* Uses a wrapped class component to create the error Boundary, but uses hooks to keep the state
40+
* in your function component.
41+
*/
42+
43+
type UseErrorBoundaryReducer = (
44+
state: ErrorState,
45+
action: StateAction
46+
) => ErrorState
47+
48+
const useErrorBoundaryReducer: UseErrorBoundaryReducer = (state, action) => {
49+
switch (action.type) {
50+
// The component did catch, update state
51+
case "catch":
52+
return {
53+
//...state,
54+
didCatch: true,
55+
// Pass the values from action.error
56+
error: action.error,
57+
}
58+
// Unknown action, return state
59+
default:
60+
return state
61+
}
62+
}
63+
64+
function useErrorBoundary(
65+
options?: UseErrorBoundaryOptions
66+
): UseErrorBoundaryState {
67+
// Reducer handling the error state
68+
const [state, dispatch] = useReducer<UseErrorBoundaryReducer>(
69+
useErrorBoundaryReducer,
70+
// Default state
71+
{
72+
didCatch: false,
73+
error: null,
74+
}
75+
)
76+
// Create ref for wrapped ErrorBoundary class
77+
const errorBoundaryWrapperRef = useRef<UseErrorBoundaryWrapper | null>(null)
78+
79+
// Get the current ref value or initialize it with a new wrapped ErrorBoundary
80+
function getWrappedErrorBoundary() {
81+
// Get current ref value
82+
let errorBoundaryWrapper = errorBoundaryWrapperRef.current
83+
84+
// Return the component when already initialized
85+
if (errorBoundaryWrapper !== null) {
86+
return errorBoundaryWrapper
87+
}
88+
89+
// Create new wrapped ErrorBoundary class with onDidCatch callback
90+
errorBoundaryWrapper = createErrorBoundary((err, errorInfo) => {
91+
// Dispatch action in case of an error
92+
dispatch({
93+
type: "catch",
94+
error: err,
95+
})
96+
97+
// call onDidCatch if provided by user
98+
if (options && options.onDidCatch) options.onDidCatch(err, errorInfo)
99+
})
100+
101+
// Update the ref with new component
102+
errorBoundaryWrapperRef.current = errorBoundaryWrapper
103+
104+
// Return the newly created component
105+
return errorBoundaryWrapper
106+
}
107+
108+
// Return the wrapped ErrorBoundary class to wrap your components in plus the error state
109+
return {
110+
ErrorBoundary: getWrappedErrorBoundary(),
111+
didCatch: state.didCatch,
112+
error: state.error,
113+
}
114+
}
115+
116+
export default useErrorBoundary

0 commit comments

Comments
 (0)