Created
July 28, 2020 21:16
-
-
Save jaredpalmer/f1a5bbbe055b6a4ea855aed5d85dce66 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { globalHistory, navigate, RedirectProps, History } from '@reach/router'; | |
import React from 'react'; | |
type ReachHistory = History & { | |
_onTransitionComplete: () => void; | |
}; | |
/** | |
* Why is `SuspenseRedirect` necessary when @reach/router has `Redirect`? | |
* | |
* Redirect's logic is to throw a `RedirectRequest` error that is caught | |
* by a `LocationProvider` in `componentDidCatch` where the redirect navigation | |
* occurs. However, once `componentDidCatch` runs, React stops suspending and | |
* renders the tree it currently has. If we use the `noThrow` prop, Redirect | |
* will render `null` and React will stop suspending. | |
* | |
* `SuspenseRedirect` avoids rendering an intermittent tree by running | |
* the redirect navigation in render and throwing an already resolved | |
* promise. This keeps React suspending and renders the tree again with | |
* the redirected location. | |
*/ | |
export const SuspenseRedirect: React.FC<RedirectProps<{}>> = ({ to }) => { | |
// navigate returns a promise, but calls pushState which runs synchronously | |
const navigatePromise = navigate(to, { replace: true }); | |
// tell reach that the transition is complete since nothing else is suspending | |
(globalHistory as ReachHistory)._onTransitionComplete(); | |
// throw the navigate promise which is now already resolved | |
throw navigatePromise; | |
// after throwing the resolved promise, React will re-render the App with the new location | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment