Next.js + SSR not updating the Context API



我们有一个简单的设置,涉及包装_app.tsx页面的Context API提供程序。我们试图实现的流程是:

  1. 在一个页面上,我们使用getServerSideProps从API收集一些数据
  2. 我们通过道具将数据发送到页面
  3. 我们从页面更新上下文(如上所述,上下文提供者正在包装_app.tsx)
  4. 上下文已更新,所有子组件都可以访问其中的数据

所以我们有一个非常标准的设置,可以在客户端正确工作。问题是更新后的上下文值没有被用来对页面进行SSR。

上下文API

const ContextValue = createContext();
const ContextUpdate = createContext();
export const ContextProvider = ({children}) => {
const [context, setContext] = useState({});

return <ContextValue.Provider value={context}>
<ContextUpdate.Provider value={setContext}>
{children}
</ContextValue.Provider>
</ContextUpdate.Provider>
}
export const useContextValue = () => {
const context = useContext(ContextValue);
return context;
}
export const useContextUpdate = () => {
const update = useContext(ContextUpdate);
return update;
}

然后我们在_app.jsx:上

...
return <ContextProvider>
<ContextApiConsumingComponent />
<Component {...pageProps} />
</Context>

在任何页面中,如果我们可以使用上面提供的钩子来更新上下文。例如:

export function Index({customData, isSSR}) {
const update = useContextUpdate();
if(isSSR) {
update({customData})
}
return (
<div>...</div>
);
}
export async function getServerSideProps(context) {
...
return {
props: { customData , isSSR}
};
};

我们可以在ContextApiConsumingComponent:中使用上下文

export function ContextApiConsumingComponent() {
const context = useContextValue()
return <pre>{JSON.stringify(context?.customData ?? {})}</pre>
}

上面的(非常简化的)代码在客户端上运行良好。但在SSR过程中,如果我们检查服务器发送到浏览器的HTML,我们会注意到<pre></pre>标签是空的,即使上下文值在__NEXT_DATA__脚本部分正确地发送到浏览器(它们在应用程序加载后准确地填充了浏览器上的数据)。

我已经建立了一个GitHub回购与最低限度的复制。

我是不是错过了什么?

据我所知,React不会等待SSR期间执行异步操作。而setState是一个异步操作。

如果您希望它在SSR期间呈现,则需要为useState钩子提供initialValue

export const ContextProvider = ({children, customData}) => {
const [context, setContext] = useState(customData);

// ...
}

不确定您是否有其他理由使用上下文将这些数据获取到api使用者中,但使用nextjs,getStaticPropsgetServerSideProps当然会馈送到路由组件中,但可能您错过了以下细节?

Component道具是活动页面,因此每当您在路线之间导航时,Component都会更改为新页面因此,您发送到组件的任何道具都将被页面接收

pageProps是一个具有初始道具的对象,这些道具是通过我们的一种数据获取方法为您的页面预加载的,否则它是一个空对象。

https://nextjs.org/docs/advanced-features/custom-app

因此,_app.tsx可以作用于路线组件通常从getStaticPropsgetServerSideProps接收的任何道具

types.ts


// or whatever union type you need to indicate that myriad of "custom datas" you might be pushing out of getStaticProps
export type MaybeCustomDataFromGetStaticProps = Map<string, string> | undefined;
export type PagePropsWithCustomData = {
customData: MaybeCustomDataFromGetStaticProps
isSSR?: boolean;
}

pages/_app.tsx

import type { PagePropsWithCustomData } from '../types';
const App = ({
Component,
pageProps: { customData, isSSR, ...pageProps},
router,
}: AppProps<PagePropsWithCustomData & Record<string, unknown>>) => {
return (
<ContextProvider value={{ isSSR, customData }}>
<ContextApiConsumingComponent />
<Component {...pageProps} />
</ContextProvider>
)
}

pages/somepage.tsx

import type { NextPage, GetServerSideProps } from "next";
import type { PagePropsWithCustomData } from '../types';
import { magicallyGetCustomData } from '../services/Api';
import { isSSR } from '../core/Routing';
type IndexRouteProps = PagePropsWithCustomData & {
isSSR?: boolean;
}
const IndexRoute:NextPage<IndexRouteProps> = ({ customData, isSSR }) => {
// no longer need to do anything with customData, or isSSR here
return (
<div>stuff</div>
);
}
export const getServerSideProps:GetServerSideProps<IndexRouteProps> = async (context) => {
const customData = await magicallyGetCustomData();

return {
props: { customData , isSSR: isSSR(context) }
};
};
export default IndexRoute

相关内容

  • 没有找到相关文章

最新更新