diff --git a/.changeset/small-squids-wash.md b/.changeset/small-squids-wash.md new file mode 100644 index 0000000000..8530430d1f --- /dev/null +++ b/.changeset/small-squids-wash.md @@ -0,0 +1,5 @@ +--- +"react-router-dom": patch +--- + +Use `pagehide` instead of `beforeunload` for ``. This has better cross-browser support, specifically on Mobile Safari. diff --git a/contributors.yml b/contributors.yml index c9fdcf2f39..627ff510ad 100644 --- a/contributors.yml +++ b/contributors.yml @@ -166,3 +166,4 @@ - xavier-lc - xcsnowcity - yuleicul +- jakkku diff --git a/packages/react-router-dom/index.tsx b/packages/react-router-dom/index.tsx index ff43c18918..27c7741f7f 100644 --- a/packages/react-router-dom/index.tsx +++ b/packages/react-router-dom/index.tsx @@ -1143,8 +1143,8 @@ function useScrollRestoration({ }; }, []); - // Save positions on unload - useBeforeUnload( + // Save positions on pagehide + usePageHide( React.useCallback(() => { if (navigation.state === "idle") { let key = (getKey ? getKey(location, matches) : null) || location.key; @@ -1241,6 +1241,28 @@ export function useBeforeUnload( }, [callback, capture]); } +/** + * Setup a callback to be fired on the window's `pagehide` event. This is + * useful for saving some data to `window.localStorage` just before the page + * refreshes. This event is better supported than beforeunload across browsers. + * + * Note: The `callback` argument should be a function created with + * `React.useCallback()`. + */ +function usePageHide( + callback: (event: PageTransitionEvent) => any, + options?: { capture?: boolean } +): void { + let { capture } = options || {}; + React.useEffect(() => { + let opts = capture != null ? { capture } : undefined; + window.addEventListener("pagehide", callback, opts); + return () => { + window.removeEventListener("pagehide", callback, opts); + }; + }, [callback, capture]); +} + /** * Wrapper around useBlocker to show a window.confirm prompt to users instead * of building a custom UI with useBlocker.