在为上下文全局存储匹配任何路由之前使用加载器?
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 上执行,提取只会发生一次,并且永远不会再次触发。
我仍然建议您在根目录的加载程序中执行此操作,以便在执行操作后可以再次获取它以保持数据更新。
在常规的 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 上执行,提取只会发生一次,并且永远不会再次触发。
我仍然建议您在根目录的加载程序中执行此操作,以便在执行操作后可以再次获取它以保持数据更新。