0

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

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

我此时正在调用 loader 中的初始数据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;
4

1 回答 1

1

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

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

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

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

hydrate(<RemixBrowser />, document);

因此,您可以在调用 hydrate 之前将其更改为 fetch。

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 上执行此操作,获取只会发生一次,并且永远不会再次触发。


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

于 2022-02-14T23:40:06.623 回答