feat(router-core,history): Global blocking status & proceed/reset actions
This PR introduces a global “blocker” state in and the callbacks needed to continue (proceed) or cancel (reset) a blocked navigation globally, to be consumed directly from the router state.
This enables controlling the blocking status out side the context of the useBlocker that was used to initiate the block.
The per-route useBlocker API remains unchanged—it now also updates the router-level state.
This change adds BLOCK and DISMISS-BLOCK actions to the history notify function.
where the BLOCK action holds a ref to the current awaited promise resolver thats currently blocking the navigation, which is then consumed through the router state.
Why?
I needed a single, root-level modal that appears whenever any navigation is blocked (unsaved changes, etc.), instead of re-instantiating the modal wherever useBlocker is used.
I expected the router to expose some global blocking state, this PR attempts to provide this functionality.
A short example of the usage:
// Some component in some route
const SomeComponentAnywhere = () => {
useBlocker({
shouldBlockFn: () => true,
withResolver: true,
})
return <div>something</div>
}
// Simple wrapper for consuming the blocking state
const useBlocking = () =>
useRouterState({
select: (state) => state.blocker,
})
// Another component in some other route
const DifferentComponenetAnywhere = () => {
const { proceed, reset, status } = useBlocking()
return (
<div>
<span>The global blocking status: {status}</span>
<button onClick={proceed}>Proceed the blocked navigation</button>
<button onClick={reset}>Reset the blocked navigation</button>
</div>
)
}
A runnable demo is available in examples/global-blocking-state.
Any feedback is appreciated!