我的项目使用 Next.js、redux 和 redux saga。以及 Axios 来管理 redux saga 的 XHR 请求。
在我的自定义应用程序(见下文)getInitialProps
中,我设置了 Axios 在客户端和服务器上发出请求时使用的基本 url。看起来这对于服务器来说工作正常(没有错误)。但是,客户端上的请求最终会尝试https://undefined/...
用作请求 url:
以下是我的相关代码,从自定义应用程序开始,一直使用 Axios 跟踪到 root saga 设置。任何指针将不胜感激。
// _app.tsx
function MyApp({
Component,
pageProps,
apiBaseUrl,
cookie,
}: AppProps & CustomAppProps) {
const store = useStore(pageProps.initialReduxState, { apiBaseUrl, cookie });
return (
<>
<Head>
<title>{formatTitle(storeEntity)}</title>
</Head>
<Provider store={store}>
<Component {...pageProps} />
</Provider>
</>
)
}
MyApp.getInitialProps = async (appContext: {
ctx: AppContext;
}): Promise<CustomAppProps> => {
let cookie: string;
let apiBaseUrl: string;
if (appContext.ctx.req) {
cookie = appContext.ctx.req.headers.cookie || '';
const host = appContext.ctx.req.headers['codomain'];
apiBaseUrl = `https://${host}/api`;
} else {
cookie = '';
apiBaseUrl = `${window.location.origin}/api`;
}
let appProps = await App.getInitialProps(appContext as any);
return {
...appProps,
cookie,
apiBaseUrl,
};
};
export default appWithTranslation(MyApp as any);
// use-store.tsx
function initStore(
preloadedState: StoreState,
{ apiBaseUrl, cookie }: CustomAppProps,
) {
const store = createStore(
reducer,
preloadedState,
composeWithDevTools(applyMiddleware(sagaMiddleware)),
);
sagaMiddleware.run(rootSaga.bind(null, { apiBaseUrl, cookie }));
return store;
}
export const initializeStore = (
preloadedState: StoreState,
{ apiBaseUrl, cookie }: CustomAppProps,
): Store<StoreState, StoreAction> & {
dispatch: unknown;
} => {
let _store = store ?? initStore(preloadedState, { apiBaseUrl, cookie });
// After navigating to a page with an initial Redux state, merge that state
// with the current state in the store, and create a new store
if (preloadedState && store) {
_store = initStore(
{
...store.getState(),
...preloadedState,
},
{ apiBaseUrl, cookie },
);
store = undefined;
}
// For SSG and SSR always create a new store
if (typeof window === 'undefined') {
return _store;
}
// Create the store once in the client
if (!store) {
store = _store;
}
return _store;
};
export function useStore(
initialState: StoreState,
{ apiBaseUrl, cookie }: CustomAppProps,
) {
const store = useMemo(
() => initializeStore(initialState, { apiBaseUrl, cookie }),
[initialState],
);
return store;
// rootSaga.ts
function* rootSaga({ apiBaseUrl, cookie }: CustomAppProps) {
const Api = new ApiService(AxiosWithApi({ apiBaseUrl, cookie }));
// We need to bind handlers to give them the 'Api' method
const fetchStoreBound = fetchStore.bind(null, Api);
yield takeLatest('API/GET_STORE', fetchStoreBound);
}
// api.tsx
export function AxiosWithApi({ cookie, apiBaseUrl }: CustomAppProps) {
const axiosOptions: AxiosRequestConfig = {
withCredentials: true,
};
return Axios.create(axiosOptions);
}
export class ApiService {
constructor(private readonly http: AxiosInstance) {}
// ...
}