From 0529002d70d68860d5a2e6a2e48888a7747dec61 Mon Sep 17 00:00:00 2001 From: Matt Brophy Date: Wed, 18 Jan 2023 12:56:19 -0500 Subject: [PATCH] Add unstable_usePrompt (#9932) --- .changeset/spicy-nails-compete.md | 5 +++++ package.json | 4 ++-- packages/react-router-dom/index.tsx | 33 +++++++++++++++++++++++++++++ 3 files changed, 40 insertions(+), 2 deletions(-) create mode 100644 .changeset/spicy-nails-compete.md diff --git a/.changeset/spicy-nails-compete.md b/.changeset/spicy-nails-compete.md new file mode 100644 index 0000000000..6ce558c255 --- /dev/null +++ b/.changeset/spicy-nails-compete.md @@ -0,0 +1,5 @@ +--- +"react-router-dom": patch +--- + +Add `unstable_usePrompt` diff --git a/package.json b/package.json index 3cb029f761..fdb6937435 100644 --- a/package.json +++ b/package.json @@ -118,10 +118,10 @@ "none": "15 kB" }, "packages/react-router-dom/dist/react-router-dom.production.min.js": { - "none": "11 kB" + "none": "11.5 kB" }, "packages/react-router-dom/dist/umd/react-router-dom.production.min.js": { - "none": "16.5 kB" + "none": "17 kB" } } } diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index e64685216d..520905d4d7 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -18,6 +18,7 @@ import { useNavigate, useNavigation, useResolvedPath, + unstable_useBlocker as useBlocker, UNSAFE_DataRouterContext as DataRouterContext, UNSAFE_DataRouterStateContext as DataRouterStateContext, UNSAFE_NavigationContext as NavigationContext, @@ -1210,6 +1211,38 @@ export function useBeforeUnload( }; }, [callback, capture]); } + +/** + * Wrapper around useBlocker to show a window.confirm prompt to users instead + * of building a custom UI with useBlocker. + * + * Warning: This has *a lot of rough edges* and behaves very differently (and + * very incorrectly in some cases) across browsers if user click addition + * back/forward navigations while the confirm is open. Use at your own risk. + */ +function usePrompt({ when, message }: { when: boolean; message: string }) { + let blocker = useBlocker(when); + + React.useEffect(() => { + if (blocker.state === "blocked" && !when) { + blocker.reset(); + } + }, [blocker, when]); + + React.useEffect(() => { + if (blocker.state === "blocked") { + let proceed = window.confirm(message); + if (proceed) { + setTimeout(blocker.proceed, 0); + } else { + blocker.reset(); + } + } + }, [blocker, message]); +} + +export { usePrompt as unstable_usePrompt }; + //#endregion ////////////////////////////////////////////////////////////////////////////////