Skip to main content

createHashRouter

还没翻译译自英文(reactrouter.com)本页 56 段都还没译

createHashRouter

Data

Summary

Reference Documentation ↗

Create a new data router that manages the application path via the URL hash.

Data Routers should not be held in React state. You should create your router once outside of the React tree and pass it to <RouterProvider>. You can use patchRoutesOnNavigation to add additional routes programmatically.

Signature

function createHashRouter(
  routes: RouteObject[],
  opts?: DOMRouterOpts,
): DataRouter

Params

routes

Application routes

opts.basename

Basename path for the application.

opts.future

Future flags to enable for the router.

opts.getContext

A function that returns an RouterContextProvider instance which is provided as the context argument to client actions, loaders and middleware. This function is called to generate a fresh context instance on each navigation or fetcher call.

import {
  createContext,
  RouterContextProvider,
} from "react-router";

const apiClientContext = createContext<APIClient>();

function createBrowserRouter(routes, {
  getContext() {
    let context = new RouterContextProvider();
    context.set(apiClientContext, getApiClient());
    return context;
  }
})

opts.hydrationData

When Server-Rendering and opting-out of automatic hydration, the hydrationData option allows you to pass in hydration data from your server-render. This will almost always be a subset of data from the StaticHandlerContext value you get back from the StaticHandler's query method:

const router = createBrowserRouter(routes, {
  hydrationData: {
    loaderData: {
      // [routeId]: serverLoaderData
    },
    // may also include `errors` and/or `actionData`
  },
});

Partial Hydration Data

You will almost always include a complete set of loaderData to hydrate a server-rendered app. But in advanced use-cases (such as Framework Mode's clientLoader), you may want to include loaderData for only some routes that were loaded/rendered on the server. This allows you to hydrate some of the routes (such as the app layout/shell) while showing a HydrateFallback component and running the loaders for other routes during hydration.

A route loader will run during hydration in two scenarios:

  1. No hydration data is provided In these cases the HydrateFallback component will render on initial hydration
  2. The loader.hydrate property is set to true This allows you to run the loader even if you did not render a fallback on initial hydration (i.e., to prime a cache with hydration data)
const router = createBrowserRouter(
  [
    {
      id: "root",
      loader: rootLoader,
      Component: Root,
      children: [
        {
          id: "index",
          loader: indexLoader,
          HydrateFallback: IndexSkeleton,
          Component: Index,
        },
      ],
    },
  ],
  {
    hydrationData: {
      loaderData: {
        root: "ROOT DATA",
        // No index data provided
      },
    },
  }
);

opts.instrumentations

Array of instrumentation objects allowing you to instrument the router and individual routes prior to router initialization (and on any subsequently added routes via route.lazy or patchRoutesOnNavigation). This is mostly useful for observability such as wrapping navigations, fetches, as well as route loaders/actions/middlewares with logging and/or performance tracing. See the docs for more information.

let router = createBrowserRouter(routes, {
  instrumentations: [logging]
});


let logging = {
  router({ instrument }) {
    instrument({
      navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
      fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
    });
  },
  route({ instrument, id }) {
    instrument({
      middleware: (impl, info) => logExecution(
        `middleware ${info.request.url} (route ${id})`,
        impl
      ),
      loader: (impl, info) => logExecution(
        `loader ${info.request.url} (route ${id})`,
        impl
      ),
      action: (impl, info) => logExecution(
        `action ${info.request.url} (route ${id})`,
        impl
      ),
    })
  }
};

async function logExecution(label: string, impl: () => Promise<void>) {
  let start = performance.now();
  console.log(`start ${label}`);
  await impl();
  let duration = Math.round(performance.now() - start);
  console.log(`end ${label} (${duration}ms)`);
}

opts.dataStrategy

Override the default data strategy of running loaders in parallel - see the docs for more information.

let router = createBrowserRouter(routes, {
  async dataStrategy({
    matches,
    request,
    runClientMiddleware,
  }) {
    const matchesToLoad = matches.filter((m) =>
      m.shouldCallHandler(),
    );

    const results: Record<string, DataStrategyResult> = {};
    await runClientMiddleware(() =>
      Promise.all(
        matchesToLoad.map(async (match) => {
          results[match.route.id] = await match.resolve();
        }),
      ),
    );
    return results;
  },
});

opts.patchRoutesOnNavigation

Lazily define portions of the route tree on navigations. See PatchRoutesOnNavigationFunction.

By default, React Router wants you to provide a full route tree up front via createBrowserRouter(routes). This allows React Router to perform synchronous route matching, execute loaders, and then render route components in the most optimistic manner without introducing waterfalls. The tradeoff is that your initial JS bundle is larger by definition — which may slow down application start-up times as your application grows.

To combat this, we introduced route.lazy in v6.9.0 which lets you lazily load the route implementation (loader, Component, etc.) while still providing the route definition aspects up front (path, index, etc.). This is a good middle ground. React Router still knows about your route definitions (the lightweight part) up front and can perform synchronous route matching, but then delay loading any of the route implementation aspects (the heavier part) until the route is actually navigated to.

In some cases, even this doesn't go far enough. For huge applications, providing all route definitions up front can be prohibitively expensive. Additionally, it might not even be possible to provide all route definitions up front in certain Micro-Frontend or Module-Federation architectures.

This is where patchRoutesOnNavigation comes in (RFC). This API is for advanced use-cases where you are unable to provide the full route tree up-front and need a way to lazily "discover" portions of the route tree at runtime. This feature is often referred to as "Fog of War", because similar to how video games expand the "world" as you move around - the router would be expanding its routing tree as the user navigated around the app - but would only ever end up loading portions of the tree that the user visited.

patchRoutesOnNavigation will be called anytime React Router is unable to match a path. The arguments include the path, any partial matches, and a patch function you can call to patch new routes into the tree at a specific location. This method is executed during the loading portion of the navigation for GET requests and during the submitting portion of the navigation for non-GET requests.

opts.window

Window object override. Defaults to the global window instance.

Returns

An initialized data router to pass to <RouterProvider>