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

Fer*_*ted 7 remix.run

在常规的 React 应用程序中,我会使用 Redux 来管理状态,在匹配 中的任何路由之前我会分派初始数据App,但是,在 Remix 中不建议使用 Redux,因此我useContext改为使用 Redux。

有没有一种方法可以调用加载器在/无需匹配任何路由之前获取初始数据(例如会话、对象等),然后将该数据存储在全局存储中context,然后可以由存储中的任何组件访问?这样,API 只会在应用程序初始化期间被调用。

我现在正在调用 的加载器中的初始数据root.tsx,获取它useLoaderData,然后将其作为道具传递给以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;
Run Code Online (Sandbox Code Playgroud)

小智 6

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

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

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

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

hydrate(<RemixBrowser />, document);
Run Code Online (Sandbox Code Playgroud)

因此,您可以将其更改为在调用 Hydro 之前进行获取。

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
    )
  });
Run Code Online (Sandbox Code Playgroud)

在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
  });
}
Run Code Online (Sandbox Code Playgroud)

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


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