@tanstack/react-router 1.33.8 → 1.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Transitioner.cjs +3 -1
- package/dist/cjs/Transitioner.cjs.map +1 -1
- package/dist/cjs/awaited.cjs +37 -15
- package/dist/cjs/awaited.cjs.map +1 -1
- package/dist/cjs/awaited.d.cts +1 -1
- package/dist/cjs/router.cjs +0 -36
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +4 -10
- package/dist/cjs/useBlocker.cjs +38 -8
- package/dist/cjs/useBlocker.cjs.map +1 -1
- package/dist/cjs/useBlocker.d.cts +18 -4
- package/dist/esm/Transitioner.js +3 -1
- package/dist/esm/Transitioner.js.map +1 -1
- package/dist/esm/awaited.d.ts +1 -1
- package/dist/esm/awaited.js +38 -16
- package/dist/esm/awaited.js.map +1 -1
- package/dist/esm/router.d.ts +4 -10
- package/dist/esm/router.js +1 -37
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/useBlocker.d.ts +18 -4
- package/dist/esm/useBlocker.js +38 -8
- package/dist/esm/useBlocker.js.map +1 -1
- package/package.json +1 -1
- package/src/Transitioner.tsx +3 -1
- package/src/awaited.tsx +40 -18
- package/src/router.ts +4 -53
- package/src/useBlocker.tsx +79 -11
package/dist/esm/useBlocker.js
CHANGED
|
@@ -1,16 +1,46 @@
|
|
|
1
1
|
import * as React from "react";
|
|
2
2
|
import { useRouter } from "./useRouter.js";
|
|
3
|
-
function useBlocker(
|
|
3
|
+
function useBlocker(blockerFnOrOpts, condition) {
|
|
4
|
+
const { blockerFn, blockerCondition } = blockerFnOrOpts ? typeof blockerFnOrOpts === "function" ? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true } : {
|
|
5
|
+
blockerFn: blockerFnOrOpts.blockerFn,
|
|
6
|
+
blockerCondition: blockerFnOrOpts.condition ?? true
|
|
7
|
+
} : { blockerFn: void 0, blockerCondition: condition ?? true };
|
|
4
8
|
const { history } = useRouter();
|
|
5
|
-
React.
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
const [resolver, setResolver] = React.useState({
|
|
10
|
+
status: "idle",
|
|
11
|
+
proceed: () => {
|
|
12
|
+
},
|
|
13
|
+
reset: () => {
|
|
14
|
+
}
|
|
15
|
+
});
|
|
16
|
+
const createPromise = () => new Promise((resolve) => {
|
|
17
|
+
setResolver({
|
|
18
|
+
status: "idle",
|
|
19
|
+
proceed: () => resolve(true),
|
|
20
|
+
reset: () => resolve(false)
|
|
21
|
+
});
|
|
9
22
|
});
|
|
23
|
+
const [promise, setPromise] = React.useState(createPromise);
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
const blockerFnComposed = async () => {
|
|
26
|
+
if (blockerFn) {
|
|
27
|
+
return await blockerFn();
|
|
28
|
+
}
|
|
29
|
+
setResolver((prev) => ({
|
|
30
|
+
...prev,
|
|
31
|
+
status: "blocked"
|
|
32
|
+
}));
|
|
33
|
+
const canNavigateAsync = await promise;
|
|
34
|
+
setPromise(createPromise);
|
|
35
|
+
return canNavigateAsync;
|
|
36
|
+
};
|
|
37
|
+
return !blockerCondition ? void 0 : history.block(blockerFnComposed);
|
|
38
|
+
}, [blockerFn, blockerCondition, history, promise]);
|
|
39
|
+
return resolver;
|
|
10
40
|
}
|
|
11
|
-
function Block({
|
|
12
|
-
useBlocker(
|
|
13
|
-
return children
|
|
41
|
+
function Block({ blockerFn, condition, children }) {
|
|
42
|
+
const resolver = useBlocker({ blockerFn, condition });
|
|
43
|
+
return children ? typeof children === "function" ? children(resolver) : children : null;
|
|
14
44
|
}
|
|
15
45
|
export {
|
|
16
46
|
Block,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useBlocker.js","sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { BlockerFn } from '@tanstack/history'\nimport type { ReactNode } from './route'\n\nexport function useBlocker(\n blockerFn
|
|
1
|
+
{"version":3,"file":"useBlocker.js","sources":["../../src/useBlocker.tsx"],"sourcesContent":["import * as React from 'react'\nimport { useRouter } from './useRouter'\nimport type { BlockerFn } from '@tanstack/history'\nimport type { ReactNode } from './route'\n\ntype BlockerResolver = {\n status: 'idle' | 'blocked'\n proceed: () => void\n reset: () => void\n}\n\ntype BlockerOpts = {\n blockerFn?: BlockerFn\n condition?: boolean | any\n}\n\nexport function useBlocker(blockerFnOrOpts?: BlockerOpts): BlockerResolver\n\n/**\n * @deprecated Use the BlockerOpts object syntax instead\n */\nexport function useBlocker(\n blockerFn?: BlockerFn,\n condition?: boolean | any,\n): BlockerResolver\n\nexport function useBlocker(\n blockerFnOrOpts?: BlockerFn | BlockerOpts,\n condition?: boolean | any,\n): BlockerResolver {\n const { blockerFn, blockerCondition } = blockerFnOrOpts\n ? typeof blockerFnOrOpts === 'function'\n ? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true }\n : {\n blockerFn: blockerFnOrOpts.blockerFn,\n blockerCondition: blockerFnOrOpts.condition ?? true,\n }\n : { blockerFn: undefined, blockerCondition: condition ?? true }\n const { history } = useRouter()\n\n const [resolver, setResolver] = React.useState<BlockerResolver>({\n status: 'idle',\n proceed: () => {},\n reset: () => {},\n })\n\n const createPromise = () =>\n new Promise<boolean>((resolve) => {\n setResolver({\n status: 'idle',\n proceed: () => resolve(true),\n reset: () => resolve(false),\n })\n })\n\n const [promise, setPromise] = React.useState(createPromise)\n\n React.useEffect(() => {\n const blockerFnComposed = async () => {\n // If a function is provided, it takes precedence over the promise blocker\n if (blockerFn) {\n return await blockerFn()\n }\n\n setResolver((prev) => ({\n ...prev,\n status: 'blocked',\n }))\n const canNavigateAsync = await promise\n\n setPromise(createPromise)\n\n return canNavigateAsync\n }\n\n return !blockerCondition ? undefined : history.block(blockerFnComposed)\n }, [blockerFn, blockerCondition, history, promise])\n\n return resolver\n}\n\nexport function Block({ blockerFn, condition, children }: PromptProps) {\n const resolver = useBlocker({ blockerFn, condition })\n return children\n ? typeof children === 'function'\n ? children(resolver)\n : children\n : null\n}\n\nexport type PromptProps = {\n blockerFn?: BlockerFn\n condition?: boolean | any\n children?: ReactNode | (({ proceed, reset }: BlockerResolver) => ReactNode)\n}\n"],"names":[],"mappings":";;AA0BgB,SAAA,WACd,iBACA,WACiB;AACjB,QAAM,EAAE,WAAW,qBAAqB,kBACpC,OAAO,oBAAoB,aACzB,EAAE,WAAW,iBAAiB,kBAAkB,aAAa,SAC7D;AAAA,IACE,WAAW,gBAAgB;AAAA,IAC3B,kBAAkB,gBAAgB,aAAa;AAAA,EAAA,IAEnD,EAAE,WAAW,QAAW,kBAAkB,aAAa;AACrD,QAAA,EAAE,YAAY;AAEpB,QAAM,CAAC,UAAU,WAAW,IAAI,MAAM,SAA0B;AAAA,IAC9D,QAAQ;AAAA,IACR,SAAS,MAAM;AAAA,IAAC;AAAA,IAChB,OAAO,MAAM;AAAA,IAAC;AAAA,EAAA,CACf;AAED,QAAM,gBAAgB,MACpB,IAAI,QAAiB,CAAC,YAAY;AACpB,gBAAA;AAAA,MACV,QAAQ;AAAA,MACR,SAAS,MAAM,QAAQ,IAAI;AAAA,MAC3B,OAAO,MAAM,QAAQ,KAAK;AAAA,IAAA,CAC3B;AAAA,EAAA,CACF;AAEH,QAAM,CAAC,SAAS,UAAU,IAAI,MAAM,SAAS,aAAa;AAE1D,QAAM,UAAU,MAAM;AACpB,UAAM,oBAAoB,YAAY;AAEpC,UAAI,WAAW;AACb,eAAO,MAAM,UAAU;AAAA,MACzB;AAEA,kBAAY,CAAC,UAAU;AAAA,QACrB,GAAG;AAAA,QACH,QAAQ;AAAA,MACR,EAAA;AACF,YAAM,mBAAmB,MAAM;AAE/B,iBAAW,aAAa;AAEjB,aAAA;AAAA,IAAA;AAGT,WAAO,CAAC,mBAAmB,SAAY,QAAQ,MAAM,iBAAiB;AAAA,KACrE,CAAC,WAAW,kBAAkB,SAAS,OAAO,CAAC;AAE3C,SAAA;AACT;AAEO,SAAS,MAAM,EAAE,WAAW,WAAW,YAAyB;AACrE,QAAM,WAAW,WAAW,EAAE,WAAW,UAAW,CAAA;AACpD,SAAO,WACH,OAAO,aAAa,aAClB,SAAS,QAAQ,IACjB,WACF;AACN;"}
|
package/package.json
CHANGED
package/src/Transitioner.tsx
CHANGED
|
@@ -23,7 +23,9 @@ export function Transitioner() {
|
|
|
23
23
|
routerState.isLoading || isTransitioning || hasPendingMatches
|
|
24
24
|
const previousIsAnyPending = usePrevious(isAnyPending)
|
|
25
25
|
|
|
26
|
-
router.
|
|
26
|
+
if (!router.isServer) {
|
|
27
|
+
router.startReactTransition = startReactTransition_
|
|
28
|
+
}
|
|
27
29
|
|
|
28
30
|
// Subscribe to location changes
|
|
29
31
|
// and try to load the new location
|
package/src/awaited.tsx
CHANGED
|
@@ -10,7 +10,9 @@ export type AwaitOptions<T> = {
|
|
|
10
10
|
promise: DeferredPromise<T>
|
|
11
11
|
}
|
|
12
12
|
|
|
13
|
-
export function useAwaited<T>({
|
|
13
|
+
export function useAwaited<T>({
|
|
14
|
+
promise,
|
|
15
|
+
}: AwaitOptions<T>): [T, DeferredPromise<T>] {
|
|
14
16
|
const router = useRouter()
|
|
15
17
|
// const rerender = React.useReducer((x) => x + 1, 0)[1]
|
|
16
18
|
|
|
@@ -56,20 +58,6 @@ export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
|
|
|
56
58
|
throw isDehydratedDeferred(promise) ? state.promise : promise
|
|
57
59
|
}
|
|
58
60
|
|
|
59
|
-
// If we are the originator of the promise,
|
|
60
|
-
// inject the state into the HTML stream
|
|
61
|
-
if (!isDehydratedDeferred(promise)) {
|
|
62
|
-
router.injectHtml(
|
|
63
|
-
`<script class='tsr_deferred_data'>window.__TSR__DEFERRED__${state.uid} = ${JSON.stringify(router.options.transformer.stringify(state))}
|
|
64
|
-
if (window.__TSR__ROUTER__) {
|
|
65
|
-
let deferred = window.__TSR__ROUTER__.getDeferred('${state.uid}');
|
|
66
|
-
if (deferred) deferred.resolve(window.__TSR__DEFERRED__${state.uid});
|
|
67
|
-
}
|
|
68
|
-
document.querySelectorAll('.tsr_deferred_data').forEach((el) => el.parentElement.removeChild(el));
|
|
69
|
-
</script>`,
|
|
70
|
-
)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
61
|
if (state.status === 'error') {
|
|
74
62
|
if (typeof document !== 'undefined') {
|
|
75
63
|
if (isServerSideError(state.error)) {
|
|
@@ -93,7 +81,7 @@ export function useAwaited<T>({ promise }: AwaitOptions<T>): [T] {
|
|
|
93
81
|
}
|
|
94
82
|
}
|
|
95
83
|
|
|
96
|
-
return [promise.__deferredState.data as any]
|
|
84
|
+
return [promise.__deferredState.data as any, promise]
|
|
97
85
|
}
|
|
98
86
|
|
|
99
87
|
export function Await<T>(
|
|
@@ -115,6 +103,40 @@ function AwaitInner<T>(
|
|
|
115
103
|
children: (result: T) => React.ReactNode
|
|
116
104
|
},
|
|
117
105
|
) {
|
|
118
|
-
const
|
|
119
|
-
|
|
106
|
+
const router = useRouter()
|
|
107
|
+
const [data, promise] = useAwaited(props)
|
|
108
|
+
const state = promise.__deferredState
|
|
109
|
+
// If we are the originator of the promise,
|
|
110
|
+
// inject the state into the HTML stream
|
|
111
|
+
return (
|
|
112
|
+
<>
|
|
113
|
+
{!isDehydratedDeferred(promise) ? (
|
|
114
|
+
<ScriptOnce
|
|
115
|
+
children={`window.__TSR__DEFERRED__${state.uid} = ${JSON.stringify(router.options.transformer.stringify(state))}
|
|
116
|
+
if (window.__TSR__ROUTER__) {
|
|
117
|
+
let deferred = window.__TSR__ROUTER__.getDeferred('${state.uid}');
|
|
118
|
+
if (deferred) deferred.resolve(window.__TSR__DEFERRED__${state.uid});
|
|
119
|
+
}
|
|
120
|
+
document.querySelectorAll('.tsr-script-once').forEach((el) => el.parentElement.removeChild(el));`}
|
|
121
|
+
/>
|
|
122
|
+
) : null}
|
|
123
|
+
{props.children(data)}
|
|
124
|
+
</>
|
|
125
|
+
)
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function ScriptOnce({
|
|
129
|
+
className,
|
|
130
|
+
children,
|
|
131
|
+
...rest
|
|
132
|
+
}: { children: string } & React.HTMLProps<HTMLScriptElement>) {
|
|
133
|
+
return (
|
|
134
|
+
<script
|
|
135
|
+
{...rest}
|
|
136
|
+
className={`tsr-script-once ${className || ''}`}
|
|
137
|
+
dangerouslySetInnerHTML={{
|
|
138
|
+
__html: children,
|
|
139
|
+
}}
|
|
140
|
+
/>
|
|
141
|
+
)
|
|
120
142
|
}
|
package/src/router.ts
CHANGED
|
@@ -483,7 +483,6 @@ export class Router<
|
|
|
483
483
|
shouldViewTransition?: boolean = undefined
|
|
484
484
|
latestLoadPromise: Promise<void> = Promise.resolve()
|
|
485
485
|
subscribers = new Set<RouterListener<RouterEvent>>()
|
|
486
|
-
injectedHtml: Array<InjectedHtmlEntry> = []
|
|
487
486
|
dehydratedData?: TDehydrated
|
|
488
487
|
viewTransitionPromise?: ControlledPromise<true>
|
|
489
488
|
manifest?: Manifest
|
|
@@ -2231,9 +2230,10 @@ export class Router<
|
|
|
2231
2230
|
return match
|
|
2232
2231
|
}
|
|
2233
2232
|
|
|
2234
|
-
|
|
2235
|
-
|
|
2236
|
-
|
|
2233
|
+
/**
|
|
2234
|
+
* @deprecated Injecting HTML directly is no longer supported. Use the new <ScriptOnce /> component instead.
|
|
2235
|
+
*/
|
|
2236
|
+
injectHtml = async (html: string | (() => Promise<string> | string)) => {}
|
|
2237
2237
|
|
|
2238
2238
|
// We use a token -> weak map to keep track of deferred promises
|
|
2239
2239
|
// that are registered on the server and need to be resolved
|
|
@@ -2250,55 +2250,6 @@ export class Router<
|
|
|
2250
2250
|
return this.registeredDeferreds.get(token)
|
|
2251
2251
|
}
|
|
2252
2252
|
|
|
2253
|
-
/**
|
|
2254
|
-
* @deprecated Please inject your own html using the `injectHtml` method
|
|
2255
|
-
*/
|
|
2256
|
-
dehydrateData = <T>(key: any, getData: T | (() => Promise<T> | T)) => {
|
|
2257
|
-
warning(
|
|
2258
|
-
false,
|
|
2259
|
-
`The dehydrateData method is deprecated. Please use the injectHtml method to inject your own data.`,
|
|
2260
|
-
)
|
|
2261
|
-
|
|
2262
|
-
if (typeof document === 'undefined') {
|
|
2263
|
-
const strKey = typeof key === 'string' ? key : JSON.stringify(key)
|
|
2264
|
-
|
|
2265
|
-
this.injectHtml(async () => {
|
|
2266
|
-
const id = `__TSR_DEHYDRATED__${strKey}`
|
|
2267
|
-
const data =
|
|
2268
|
-
typeof getData === 'function' ? await (getData as any)() : getData
|
|
2269
|
-
return `<script id='${id}' suppressHydrationWarning>
|
|
2270
|
-
window["__TSR_DEHYDRATED__${escapeJSON(
|
|
2271
|
-
strKey,
|
|
2272
|
-
)}"] = ${JSON.stringify(this.options.transformer.stringify(data))}
|
|
2273
|
-
</script>`
|
|
2274
|
-
})
|
|
2275
|
-
|
|
2276
|
-
return () => this.hydrateData<T>(key)
|
|
2277
|
-
}
|
|
2278
|
-
|
|
2279
|
-
return () => undefined
|
|
2280
|
-
}
|
|
2281
|
-
|
|
2282
|
-
/**
|
|
2283
|
-
* @deprecated Please extract your own data from scripts injected using the `injectHtml` method
|
|
2284
|
-
*/
|
|
2285
|
-
hydrateData = <T = unknown>(key: any) => {
|
|
2286
|
-
warning(
|
|
2287
|
-
false,
|
|
2288
|
-
`The hydrateData method is deprecated. Please use the extractHtml method to extract your own data.`,
|
|
2289
|
-
)
|
|
2290
|
-
|
|
2291
|
-
if (typeof document !== 'undefined') {
|
|
2292
|
-
const strKey = typeof key === 'string' ? key : JSON.stringify(key)
|
|
2293
|
-
|
|
2294
|
-
return this.options.transformer.parse(
|
|
2295
|
-
window[`__TSR_DEHYDRATED__${strKey}` as any] as unknown as string,
|
|
2296
|
-
) as T
|
|
2297
|
-
}
|
|
2298
|
-
|
|
2299
|
-
return undefined
|
|
2300
|
-
}
|
|
2301
|
-
|
|
2302
2253
|
dehydrate = (): DehydratedRouter => {
|
|
2303
2254
|
const pickError =
|
|
2304
2255
|
this.options.errorSerializer?.serialize ?? defaultSerializeError
|
package/src/useBlocker.tsx
CHANGED
|
@@ -3,25 +3,93 @@ import { useRouter } from './useRouter'
|
|
|
3
3
|
import type { BlockerFn } from '@tanstack/history'
|
|
4
4
|
import type { ReactNode } from './route'
|
|
5
5
|
|
|
6
|
+
type BlockerResolver = {
|
|
7
|
+
status: 'idle' | 'blocked'
|
|
8
|
+
proceed: () => void
|
|
9
|
+
reset: () => void
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
type BlockerOpts = {
|
|
13
|
+
blockerFn?: BlockerFn
|
|
14
|
+
condition?: boolean | any
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export function useBlocker(blockerFnOrOpts?: BlockerOpts): BlockerResolver
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* @deprecated Use the BlockerOpts object syntax instead
|
|
21
|
+
*/
|
|
22
|
+
export function useBlocker(
|
|
23
|
+
blockerFn?: BlockerFn,
|
|
24
|
+
condition?: boolean | any,
|
|
25
|
+
): BlockerResolver
|
|
26
|
+
|
|
6
27
|
export function useBlocker(
|
|
7
|
-
|
|
8
|
-
condition
|
|
9
|
-
):
|
|
28
|
+
blockerFnOrOpts?: BlockerFn | BlockerOpts,
|
|
29
|
+
condition?: boolean | any,
|
|
30
|
+
): BlockerResolver {
|
|
31
|
+
const { blockerFn, blockerCondition } = blockerFnOrOpts
|
|
32
|
+
? typeof blockerFnOrOpts === 'function'
|
|
33
|
+
? { blockerFn: blockerFnOrOpts, blockerCondition: condition ?? true }
|
|
34
|
+
: {
|
|
35
|
+
blockerFn: blockerFnOrOpts.blockerFn,
|
|
36
|
+
blockerCondition: blockerFnOrOpts.condition ?? true,
|
|
37
|
+
}
|
|
38
|
+
: { blockerFn: undefined, blockerCondition: condition ?? true }
|
|
10
39
|
const { history } = useRouter()
|
|
11
40
|
|
|
12
|
-
React.
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
const [resolver, setResolver] = React.useState<BlockerResolver>({
|
|
42
|
+
status: 'idle',
|
|
43
|
+
proceed: () => {},
|
|
44
|
+
reset: () => {},
|
|
15
45
|
})
|
|
46
|
+
|
|
47
|
+
const createPromise = () =>
|
|
48
|
+
new Promise<boolean>((resolve) => {
|
|
49
|
+
setResolver({
|
|
50
|
+
status: 'idle',
|
|
51
|
+
proceed: () => resolve(true),
|
|
52
|
+
reset: () => resolve(false),
|
|
53
|
+
})
|
|
54
|
+
})
|
|
55
|
+
|
|
56
|
+
const [promise, setPromise] = React.useState(createPromise)
|
|
57
|
+
|
|
58
|
+
React.useEffect(() => {
|
|
59
|
+
const blockerFnComposed = async () => {
|
|
60
|
+
// If a function is provided, it takes precedence over the promise blocker
|
|
61
|
+
if (blockerFn) {
|
|
62
|
+
return await blockerFn()
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
setResolver((prev) => ({
|
|
66
|
+
...prev,
|
|
67
|
+
status: 'blocked',
|
|
68
|
+
}))
|
|
69
|
+
const canNavigateAsync = await promise
|
|
70
|
+
|
|
71
|
+
setPromise(createPromise)
|
|
72
|
+
|
|
73
|
+
return canNavigateAsync
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return !blockerCondition ? undefined : history.block(blockerFnComposed)
|
|
77
|
+
}, [blockerFn, blockerCondition, history, promise])
|
|
78
|
+
|
|
79
|
+
return resolver
|
|
16
80
|
}
|
|
17
81
|
|
|
18
|
-
export function Block({
|
|
19
|
-
useBlocker(
|
|
20
|
-
return children
|
|
82
|
+
export function Block({ blockerFn, condition, children }: PromptProps) {
|
|
83
|
+
const resolver = useBlocker({ blockerFn, condition })
|
|
84
|
+
return children
|
|
85
|
+
? typeof children === 'function'
|
|
86
|
+
? children(resolver)
|
|
87
|
+
: children
|
|
88
|
+
: null
|
|
21
89
|
}
|
|
22
90
|
|
|
23
91
|
export type PromptProps = {
|
|
24
|
-
|
|
92
|
+
blockerFn?: BlockerFn
|
|
25
93
|
condition?: boolean | any
|
|
26
|
-
children?: ReactNode
|
|
94
|
+
children?: ReactNode | (({ proceed, reset }: BlockerResolver) => ReactNode)
|
|
27
95
|
}
|