在为上下文全局存储匹配任何路由之前使用加载器?

use loader before matching any route for context global store?

在常规的 React 应用程序中,我会使用 Redux 来管理状态,我会在 App 中匹配任何路由之前发送初始数据,但是,在 Remix 中不建议使用 Redux,所以我我正在使用 useContext

有没有办法调用加载程序来获取初始数据(例如会话、对象等)before/without 必须匹配任何路由,然后将该数据存储在 context 全局存储中然后可以被商店内的任何组件访问?这样,API 只会在应用程序初始化期间被调用。

我现在正在调用 root.tsx 的加载器中的初始数据,用 useLoaderData 获取它,然后将它作为 prop 传递给 StoreProvider 以将其发送到全局状态,但是,我认为不应该那样做。

export let loader: LoaderFunction = async ({ request }) => {
  let user = await getUser(request);
  const products = await db.product.findMany();
  return { user: user?.username, products };
};

function App() {
  const data = useLoaderData<LoaderData>();

  return (
    <html lang="en">
      ...
      <StoreProvider initData={data}>
        <body>
          ...
          <Outlet />
          <ScrollRestoration />
          <Scripts />
          {process.env.NODE_ENV === "development" && <LiveReload />}
        </body>
      </StoreProvider>
    </html>
  );
}

export default App;

我认为在根路由加载器上进行数据加载是最好的方法。

如果您不喜欢这种方法,您也可以在 entry.server 和 entry.client 上获取。

例如在 entry.client 你可能有这样的东西:

import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";

hydrate(<RemixBrowser />, document);

因此您可以将其更改为在调用 hydrate 之前进行提取。

import { hydrate } from "react-dom";
import { RemixBrowser } from "remix";

fetch(YOUR_API_ENDPOINT)
  .then(response => response.json())
  .then(data => {
    hydrate(
      <YourContextProvider value={data}>
        <RemixBrowser />
      </YourContextProvider>,
      document
    )
  });

并且在 entry.server 中,您可以将 handleRequest 函数更改为如下内容:

import { renderToString } from "react-dom/server";
import { RemixServer } from "remix";
import type { EntryContext } from "remix";

export default async function handleRequest(
  request: Request,
  responseStatusCode: number,
  responseHeaders: Headers,
  remixContext: EntryContext
) {
  let response = await fetch(YOUR_API_ENDPOINT)
  let data = await response.json()

  let markup = renderToString(
    <YourContextProvider value={data}>
      <RemixServer context={remixContext} url={request.url} />
    </YourContextProvider>
  );

  responseHeaders.set("Content-Type", "text/html");

  return new Response("<!DOCTYPE html>" + markup, {
    status: responseStatusCode,
    headers: responseHeaders
  });
}

通过在 entry.client 和 entry.server 上执行,提取只会发生一次,并且永远不会再次触发。


我仍然建议您在根目录的加载程序中执行此操作,以便在执行操作后可以再次获取它以保持数据更新。