【问题标题】:ReactJs, Typescript protected route with HOC as functional componentReactJs,以 HOC 作为功能组件的 Typescript 保护路由
【发布时间】:2020-10-11 10:53:29
【问题描述】:

我不知道如何构建一个简单的 HOC 来保护组件,即确保用户在组件呈现之前登录。

这是我构建 HOC 以保护组件的尝试(目前还不行)。

export default function ProtectedRoute(Component: React.ComponentType) {
  return () => {
    const authUser = useSession();
    const router = useRouter();

    if (typeof window === 'undefined') {
      return null;
    }

    if (!authUser) {
      router.push('/login');
      return null;
    }
    return <Component />;
  };
}

这是我要保护的组件。

const AppLayout = ({
  children,
  title = 'This is the default title',
}: Props): React.ReactElement => {
return (
     ...
      <main className="bg-nord-bg2">
        <div className="min-h-screen w-full max-w-screen-xl mx-auto py-6 sm:px-6 lg:px-8">
          <Alert />
          {children}
        </div>
      </main>
)
}

export default ProtectedRoute(AppLayout);

这就是我如何调用布局组件:

function App({ Component, pageProps }: any) {
  return (
        <Layout title="Meno">
          <Component {...pageProps} />
        </Layout>
      ) 
}

export default App;

这是我的错误信息: Argument of type '({ children, title, }: Props) =&gt; React.ReactElement' is not assignable to parameter of type 'ComponentType&lt;{}&gt;'. Type '({ children, title, }: Props) =&gt; React.ReactElement' is not assignable to type 'FunctionComponent&lt;{}&gt;'. Types of parameters '__0' and 'props' are incompatible. Property 'title' is missing in type '{ children?: ReactNode; }' but required in type 'Props'

你有什么提示吗?

【问题讨论】:

    标签: javascript reactjs next.js


    【解决方案1】:

    简而言之,您的AppLayout 不会返回ReactElement,而是返回FC(功能组件)。

    我建议cheatsheat 用于类型差异。


    这是一个工作示例项目,可以在 here 找到。

    components/AppContext/index.tsx

    import { createContext, useCallback, useContext, useState } from "react";
    import type { FC, ReactNode } from "react";
    
    type Context = {
      isAuthenticated: Boolean;
      handleAuthentication: () => void;
    }
    
    const AppContext = createContext<Context>({
      isAuthenticated: false,
      handleAuthentication: () => {},
    });
    
    const AppContextProvider: FC<{ children: ReactNode }> = ({ children }) => {
      const [isAuthenticated, setAuthentication] = useState(false);
    
      const handleAuthentication = useCallback(() => {
        setAuthentication((prevState) => !prevState);
      }, []);
    
      return (
        <AppContext.Provider
          value={{
            isAuthenticated,
            handleAuthentication,
          }}
        >
          {children}
          {isAuthenticated && (
            <button type="button" onClick={handleAuthentication}>
              Log out
            </button>
          )}
        </AppContext.Provider>
      );
    };
    
    export const useAppContext = () => useContext(AppContext);
    
    export default AppContextProvider;
    

    components/withAuthentication/index.tsx

    import { useRouter } from "next/router";
    import { useEffect } from "react";
    import { useAppContext } from "../AppContext";
    import type { FC } from "react";
    
    type withAuthenticationFn = (Component: FC) => FC;
    
    const withAuthentication: withAuthenticationFn = (Component) => {
      const Authenticated: FC = (): JSX.Element | null => {
        const { isAuthenticated } = useAppContext();
        const router = useRouter();
    
        useEffect(() => {
          if (!isAuthenticated) router.push("/login");
        });
    
        return isAuthenticated ? <Component /> : null;
      };
    
      return Authenticated;
    };
    
    export default withAuthentication;
    

    pages/_app.tsx

    import AppContextProvider from "../components/AppContext";
    import type { FC } from "react";
    import type { AppProps } from "next/app";
    
    const App: FC<AppProps> = ({ Component, pageProps }) => (
      <AppContextProvider>
        <Component {...pageProps} />
      </AppContextProvider>
    );
    
    export default App;
    

    pages/about.tsx

    import Head from "next/head";
    import Link from "next/link";
    import withAuthentication from "../components/withAuthentication";
    import type { NextPage } from "next";
    
    const AboutPage: NextPage = () => (
      <>
        <Head>
          <title> About - Next App</title>
        </Head>
        <h1>About</h1>
        <p>This is the about page</p>
        <p>
          <Link href="/">
            <a>Go home</a>
          </Link>
        </p>
      </>
    );
    
    export default withAuthentication(AboutPage);
    

    pages/index.tsx

    import Link from "next/link";
    import Head from "next/head";
    import withAuthentication from "../components/withAuthentication";
    import type { NextPage } from "next";
    
    const IndexPage: NextPage = () => (
      <>
        <Head>
          <title> Dashboard - Next App</title>
        </Head>
        <h1>Hello Next.js ?</h1>
        <p>
          <Link href="/about">
            <a>About</a>
          </Link>
        </p>
      </>
    );
    
    export default withAuthentication(IndexPage);
    

    pages/login.tsx

    import Head from "next/head";
    import { useRouter } from "next/router";
    import { useEffect } from "react";
    import { useAppContext } from "../components/AppContext";
    import type { NextPage } from "next";
    
    const Login: NextPage = () => {
      const { isAuthenticated, handleAuthentication } = useAppContext();
      const router = useRouter();
    
      useEffect(() => {
        if (isAuthenticated) router.push("/");
      }, [isAuthenticated]);
    
      return (
        <>
          <Head>
            <title> Login - Next App</title>
          </Head>
          <button type="button" onClick={handleAuthentication}>
            Log in
          </button>
        </>
      );
    };
    
    export default Login;
    

    【讨论】:

      猜你喜欢
      • 2018-04-15
      • 2019-08-13
      • 1970-01-01
      • 1970-01-01
      • 2021-04-30
      • 2020-01-11
      • 2021-10-27
      • 2019-08-31
      • 1970-01-01
      相关资源
      最近更新 更多