NextJS客户端路由TypeScript私有路由布局不显示问题
NextJS 私有路由HOC导致布局组件无法加载的问题
我在NextJS中尝试不使用服务端渲染实现私有路由组件,用权限校验HOC(withAuth)包裹页面组件后,发现页面的布局组件(通过getLayout设置)无法正常显示,移除HOC包裹后布局恢复正常。
withAuth HOC代码
/* eslint-disable react-hooks/exhaustive-deps */ import { useRouter } from 'next/router'; import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import Login from '../pages/login'; import { getAuthState, LoginStatusEnum } from '../store/slices/authSlice.store'; const withAuth = (Component: React.FC) => { const AuthenticatedComponent = (props: any) => { const router = useRouter(); const { loggedStatus } = useSelector(getAuthState); // TODO token auth needs to be added useEffect(() => { // If user is not logged in, return login component if (loggedStatus !== LoginStatusEnum.LOGGED_IN) { router.push('/login'); } }, [loggedStatus]); // If user is logged in, return original component return <Component {...props} />; }; return AuthenticatedComponent; }; export default withAuth;
Dashboard页面组件代码
import Head from 'next/head'; import withAuth from '../components/PrivateRoute'; import getLayout, { LayoutOptions } from '../utils/getLayout/getLayout.util'; import { NextPageWithLayout } from './_app'; const Dashboard: NextPageWithLayout = ({ ...props }) => { return ( <> <Head> <title>Everyone Active - Dashboard</title> </Head> <div>Welcome to the dashboard</div> </> ); }; Dashboard.getLayout = getLayout(LayoutOptions.STANDARD); Dashboard.defaultProps = {}; Dashboard.propTypes = {}; export default withAuth(Dashboard);
getLayout工具函数代码
const getLayout = (name: LayoutOptions) => { // eslint-disable-next-line react/display-name return (page: ReactElement) => { switch (name) { case LayoutOptions.STANDARD: return <StandardLayout>{page}</StandardLayout>; case LayoutOptions.FULL_WIDTH: return <FullWidthLayout>{page}</FullWidthLayout>; default: throw new Error(`Layout "${name}" does not exist.`); } }; }; export default getLayout;
StandardLayout组件代码
const StandardLayout: React.FC<PropsWithChildren<StandardLayoutProps>> = ({ children, ...props }) => { return ( <div className="flex flex-col h-screen justify-between"> <div> <Header /> <div className="mt-4 container grid md:grid-cols-12 mx-auto"> <aside className="col-span-3 order-2 md:order-1">Sidebar</aside> <main className="col-span-9 order-1 md:order-2">{children}</main> </div> </div> <Footer /> </div> ); }; StandardLayout.defaultProps = {}; StandardLayout.propTypes = {}; export default StandardLayout;
问题原因
你的withAuth HOC返回的AuthenticatedComponent没有继承原组件Dashboard上的getLayout静态属性。NextJS通过读取页面组件的getLayout方法来应用布局,HOC包裹后导出的是AuthenticatedComponent,它没有这个属性,所以布局无法加载。
修复方法
在HOC中把原组件的getLayout属性复制到返回的AuthenticatedComponent上,同时适配TypeScript类型:
修改后的withAuth代码:
/* eslint-disable react-hooks/exhaustive-deps */ import { useRouter } from 'next/router'; import React, { useEffect } from 'react'; import { useSelector } from 'react-redux'; import { getAuthState, LoginStatusEnum } from '../store/slices/authSlice.store'; import { NextPageWithLayout } from '../pages/_app'; // 导入你的页面类型 const withAuth = <T extends NextPageWithLayout>(Component: T) => { const AuthenticatedComponent = (props: any) => { const router = useRouter(); const { loggedStatus } = useSelector(getAuthState); useEffect(() => { if (loggedStatus !== LoginStatusEnum.LOGGED_IN) { router.push('/login'); } }, [loggedStatus]); return <Component {...props} />; }; // 复制原组件的getLayout静态属性 AuthenticatedComponent.getLayout = Component.getLayout; return AuthenticatedComponent as T; }; export default withAuth;
修改后,AuthenticatedComponent继承了原页面组件的getLayout方法,NextJS就能正常加载布局了。另外你之前导入的Login组件未使用,可以考虑删除以减少冗余。
内容的提问来源于stack exchange,提问作者James
相关产品推荐
相关产品推荐

