这是我参与「第五届青训营 」伴学笔记创作活动的第 9 天。我们 Bocchi 小组采用 Nextjs 开发这次掘金站点的前端,这几天我们尝试与后端对接,这篇文章就来简要记录下实现过程(具体代码看下方仓库)。
仓库地址: github.com/Bocchi-Deve…
思路
预数据获取是 Nextjs 开发很重要的一环,我们可以采用 getInitialProps来进行数据获取,同时考虑到前端程序的健壮性,如果后端服务出现异常,我们前端也应该展示出无数据异常页面,而不是直接坏掉。
实现
为了能够正确获取预数据,我们可以在 _app.tsx 中写出如下代码:
App.getInitialProps = async (props: AppContext) => {
const ctx = props.ctx
const data = await AggregateApi.aggregateInfoRequest() // 获取预数据
const appProps = await (async () => {
try {
return await NextApp.getInitialProps(props)
} catch (e) {
if (!data?.user) {
throw e
}
if (ctx.res) {
ctx.res.statusCode = 466
ctx.res.statusMessage = 'No Data'
}
return null
}
})()
return {
...appProps,
initData: data,
}
}
我们应该在 App 组件中进行判断,看是否成功获取到数据,如没有获取到就展示 <NoDataErrorView /> 组件。同时我们也应该把数据注入到对应的 Provider中,方便后续获取。另外这里的 <Wrapper/>组件主要用来布局页面,以及 SEO 相关的功能,这里就不过多叙述了。 具体实现如下:
const App: FC<AppProps & Record<'initData', IAggregate>> = ({
Component,
pageProps,
initData,
}) => {
const Inner = useMemo(() => {
return initData.user ? (
<Wrapper>
<Component {...pageProps} />
</Wrapper>
) : (
<NoDataErrorView />
)
}, [Component, initData, pageProps])
return (
<RootStoreProvider>
<InitialContextProvider value={initData}>{Inner}</InitialContextProvider>
</RootStoreProvider>
)
}
import type { FC, PropsWithChildren } from 'react'
import { createContext, memo, useMemo } from 'react'
import type { IAggregate } from '~/types/api/aggregate'
export const InitialContext = createContext({} as IAggregate)
export const InitialContextProvider: FC<
PropsWithChildren<{ value: IAggregate }>
> = memo((props) => {
return (
<InitialContext.Provider
value={useMemo(() => ({ ...props.value }), [props.value])}
>
{props.children}
</InitialContext.Provider>
)
})
无数据页实现如下:
import type { FC } from 'react'
import { API_URL } from '~/constants/env'
import { ErrorView } from '.'
export const NoDataErrorView: FC = () => {
return (
<ErrorView
noSeo
statusCode={'无数据'}
showBackButton={false}
description={
<>
<p>出现这个错误表示未获取到初始数据</p>
<p>可能是 API 接口地址配置不正确,或者后端服务出现异常</p>
<p>API 地址:{API_URL}</p>
</>
}
/>
)
}