【问题标题】:Next.js authentication pattern/strategy for private pages and a layoutNext.js 私有页面和布局的身份验证模式/策略
【发布时间】:2021-10-09 11:55:55
【问题描述】:

假设我有 10 个页面,其中 5 个是私有的,5 个是公共的。在 Next.js 文档中,我找到了一个巧妙的解决方案,我们在 getServerSideProps 中添加了一个获取用户的调用,但我也有一个布局。在我的情况下,它是一个标题,我在 _app.js 中添加。我想在所有页面上显示标题。在该标题中,我有一个头像或登录按钮。为了在标题中实现这一点,我在 _app 的 useEffect 中添加了一个用户调用。但是现在当用户进入私人页面时,我将再次发出请求。 为了摆脱两次拨打相同的电话,我做了:

  1. 在 _app.js 中调用 useEffect 后,我​​将用户状态存储在 Redux 中。 (这个调用我们需要决定在标题中显示什么)
  2. 我创建了一个 PrivateRoute HOC 组件并将其添加到应该是私有的页面中。

我的问题是:

  1. 这种方法是否足够好,它不会破坏 Next.js 中的某些内容吗?还是可以进行两次调用以更好地实施?
  2. 我能否像验证页面一样验证服务器上的用户,但验证 _app.js 中的组件(在我的例子中是标题)? (据我了解,不建议将 getInitialProp 添加到 _app 中)

PrivateRoute HOC 组件

import { useRouter } from 'next/router';
import { useSelector } from 'react-redux';
import { userSelector } from '../../redux/selectors';

interface IPrivateRoute {
  children: React.ReactNode;
}

const PrivateRoute: React.FC<IPrivateRoute> = (props: IPrivateRoute) => {
  const { children } = props;
  const router = useRouter();
  const user = useSelector(userSelector);

  if (typeof window !== 'undefined' && !user.isUserLoading && user.isAuthenticated === false) {
    router.push('/login');
  }

  if (!user.isUserLoading && user.isAuthenticated === true) {
    return <>{children}</>;
  }

  return <></>;
};

export default PrivateRoute;

【问题讨论】:

    标签: reactjs authentication next.js


    【解决方案1】:
    1. 您所做的一切正常,不会破坏 Next。如果更容易或更一致,您可以拨打两个电话。如果您不需要,则不应该这样做,但是 - 它对较小的网站的影响最小。
    2. 或者,您可以为用户/角色/通过服务器端响应进行身份验证设置全局状态。然后创建一个钩子来读取您的全局状态以获取当前用户详细信息。最后,您将在整个应用程序中使用挂钩来根据用户详细信息进行布局更改。您使用 Redux,但也可以使用 React 上下文。

    至少我会建议您始终返回相应的状态 - 就目前而言,您可以返回一个空白屏幕。以下是我将如何重构您的类型和 HOC 代码,使其始终返回一些内容。

    import { useRouter } from 'next/router';
    import { useSelector } from 'react-redux';
    import { userSelector } from '../../redux/selectors';
    
    const PrivateRoute: React.FC = ({ children }) => {
      const { push }= useRouter();
      const {isAuthenticated, isUserLoading } = useSelector(userSelector);
      if (typeof window !== 'undefined') return null; // wait for mount
      if(isUserLoading) return 'loading screen...'; // mounted & loading
      if(!isAuthenticated) return push('/login'); // mounted & not loading & not authenticated
      return children; // mounted & not loading & authenticated
    };
    
    export default PrivateRoute;
    

    children 包含在 FC 类型中,因此您无需设置 children 属性。

    【讨论】:

    • 感谢您的回复! React Context 肯定可以在这里工作,但是我习惯了 Redux,尤其是使用 Redux Tool Kit,代码量似乎比 React Context 还要少。我也已经用它来调用snackbar 等。在你重构之后,它看起来更体面,但我想我会选择使用getServerSideProps 的文档中的示例。那么让它成为两个调用,一个在 _app.js 中,另一个在每个私有页面上。正如你提到的,这只是一个小项目,所以我认为没关系。 =)
    猜你喜欢
    • 2020-07-03
    • 2017-11-11
    • 2019-06-12
    • 2021-12-24
    • 2017-05-20
    • 2019-03-23
    • 1970-01-01
    • 2019-12-14
    • 2019-04-30
    相关资源
    最近更新 更多