Skip to content

Instantly share code, notes, and snippets.

@kale1d0code
Created December 6, 2023 15:47
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kale1d0code/6fec7abec7c921d67e7d4463f155d17d to your computer and use it in GitHub Desktop.
Save kale1d0code/6fec7abec7c921d67e7d4463f155d17d to your computer and use it in GitHub Desktop.
import {
createRouter,
createBrowserHistory,
UNSAFE_ErrorResponseImpl as ErrorResponseImpl
} from "@remix-run/router";
import {
RouteObject,
UNSAFE_mapRouteProperties as mapRouteProperties,
} from "react-router";
import type {
FutureConfig as RouterFutureConfig,
HydrationState,
Router as RemixRouter,
History
} from "@remix-run/router";
declare global {
var __staticRouterHydrationData: HydrationState | undefined;
}
interface DOMRouterOpts {
basename?: string;
future?: Partial<Omit<RouterFutureConfig, "v7_prependBasename">>;
hydrationData?: HydrationState;
window?: Window;
}
const createHistoryRouter = (history: History) => (
routes: RouteObject[],
opts?: DOMRouterOpts
): RemixRouter => {
const router = createRouter({
basename: opts?.basename,
future: {
...opts?.future,
v7_prependBasename: true,
},
history: history ?? createBrowserHistory({ window: opts?.window }),
hydrationData: opts?.hydrationData || parseHydrationData(),
routes,
mapRouteProperties,
}).initialize();
return router;
}
function parseHydrationData(): HydrationState | undefined {
let state = window?.__staticRouterHydrationData;
if (state && state.errors) {
state = {
...state,
errors: deserializeErrors(state.errors),
};
}
return state;
}
function deserializeErrors(
errors: RemixRouter["state"]["errors"]
): RemixRouter["state"]["errors"] {
if (!errors) return null;
let entries = Object.entries(errors);
let serialized: RemixRouter["state"]["errors"] = {};
for (let [key, val] of entries) {
// Hey you! If you change this, please change the corresponding logic in
// serializeErrors in react-router-dom/server.tsx :)
if (val && val.__type === "RouteErrorResponse") {
serialized[key] = new ErrorResponseImpl(
val.status,
val.statusText,
val.data,
val.internal === true
);
} else if (val && val.__type === "Error") {
// Attempt to reconstruct the right type of Error (i.e., ReferenceError)
if (val.__subType) {
let ErrorConstructor = window[val.__subType];
if (typeof ErrorConstructor === "function") {
try {
//@ts-expect-error
let error = new ErrorConstructor(val.message);
// Wipe away the client-side stack trace. Nothing to fill it in with
// because we don't serialize SSR stack traces for security reasons
error.stack = "";
serialized[key] = error;
} catch (e) {
// no-op - fall through and create a normal Error
}
}
}
if (serialized[key] == null) {
let error = new Error(val.message);
// Wipe away the client-side stack trace. Nothing to fill it in with
// because we don't serialize SSR stack traces for security reasons
error.stack = "";
serialized[key] = error;
}
} else {
serialized[key] = val;
}
}
return serialized;
}
export {
createHistoryRouter
}
I haven't shown my store configuration file however
all you need to do is call createReduxHistoryContext as normal
and call the wrapHistory function on the history instance that is returned from createReduxHistoryContext.
you can then pass the modified history instance that wrapHistory returned into your react-router 6.4 router
(wrap history adds the encodeLocation and createURL methods to the history instance that is required by the react-router 6.4 routers)
//@ts-expect-error in the router file is due to me not being able to find the correct types for history with encodeLocation and createURL from the react-router library.
import { ComponentType } from "react";
import { createHistoryRouter } from "./custom-history-router.ts";
import routes from "./routes";
import { RouterProvider } from "react-router";
import { ReduxNavtionHistory } from "../../data/utilities/wrap-history";
export type PropsWithHistory<P> = P & { history: ReduxNavtionHistory };
const Router: ComponentType<PropsWithHistory<{}>> = ({ history }) => {
//@ts-expect-error
const browserRouter = createHistoryRouter(history);
const router = browserRouter(routes, {
future: { v7_normalizeFormMethod: true },
});
return <RouterProvider router={router} />;
};
export default Router;
import { createPath } from "@remix-run/router";
import { To } from "react-router";
import { IHistoryContext } from "redux-first-history";
export function invariant(value: boolean, message?: string): asserts value;
export function invariant<T>(
value: T | null | undefined,
message?: string,
): asserts value is T;
export function invariant(value: any, message?: string) {
if (value === false || value === null || typeof value === "undefined") {
throw new Error(message);
}
}
function createURL(to: To): URL {
// window.location.origin is "null" (the literal string value) in Firefox
// under certain conditions, notably when serving from a local HTML file
// See https://bugzilla.mozilla.org/show_bug.cgi?id=878297
let base =
window.location.origin !== "null"
? window.location.origin
: window.location.href;
let href = typeof to === "string" ? to : createPath(to);
invariant(
base,
`No window.location.(origin|href) available to create URL for href: ${href}`,
);
return new URL(href, base);
}
function encodeLocation(to: To) {
// Encode a Location the same way window.location would
let url = createURL(to);
return {
pathname: url.pathname,
search: url.search,
hash: url.hash,
};
}
type HF = IHistoryContext["createReduxHistory"];
export type ReduxNavtionHistory = ReturnType<HF>;
const wrapHistory = (history: ReduxNavtionHistory): ReduxNavtionHistory => {
const newHistory: ReduxNavtionHistory = Object.assign({
...history,
encodeLocation,
createURL,
});
return newHistory;
};
export default wrapHistory;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment