Skip to content

Instantly share code, notes, and snippets.

@jaredpalmer
Created July 28, 2020 21:16
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaredpalmer/f1a5bbbe055b6a4ea855aed5d85dce66 to your computer and use it in GitHub Desktop.
Save jaredpalmer/f1a5bbbe055b6a4ea855aed5d85dce66 to your computer and use it in GitHub Desktop.
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