代码之家  ›  专栏  ›  技术社区  ›  user158

在Nextjs嵌套布局中,AuthContext用户在页面刷新时为null

  •  0
  • user158  · 技术社区  · 3 年前

    我有以下代码 <AuthContext> 提供认证对象和方法, <ProtectedRoute> 保护身份验证所需路由的组件。

    但问题是,当我登录并刷新经过身份验证的页面时, user 从中提取的对象 useAuth 挂钩返回 null ,但如果我使用 next/link 它工作良好,并且保留了用户对象。

    AuthContext.tsx

    const AuthContext = createContext<any>({})
    
    export const useAuth = () => useContext(AuthContext)
    
    type Props = {
      children: React.ReactNode
    }
    
    const LOGIN = gql`...`
    
    export const AuthContextProvider = ({ children }: Props) => {
      const [user, setUser] = useState<object | null>(null)
      const [loginUser, { data, loading, error }] = useMutation(LOGIN)
      const router = useRouter()
    
      useEffect(() => {
        // in case of first login set user and token
        // push user to route he belongs
        if (data != null) {
          if (data.authenticate.jwt !== null) {
            console.log('Setting user with JWT decoding...')
            const decoded = jwt_decode(data.authenticate.jwt)
            setUser({
              role: decoded.role,
              id: decoded.id,
            })
            localStorage.setItem('token', data.authenticate.jwt)
          }
        }
    
        const token = localStorage.getItem('token')
        // if token is present set the user object
        if (token !== null) {
          console.log('Token present')
          const decoded = jwt_decode(token)
          // console.log(user)
          console.log(`Decoded token : ${JSON.stringify(decoded)}`)
          setUser({
            role: decoded.role,
            id: decoded.id,
          })
        } else {
          setUser(null)
        }
      }, [data])
    
      const login = async (username: string, password: string) => {
        loginUser({
          variables: {
            username,
            password,
          },
        })
      }
    
      const logOut = async () => {
        console.log('Logging out ...')
        setUser(null)
        data = null
        localStorage.removeItem('token')
      }
    
      // if (error) return <p>Submission error! ${error.message}</p>
    
      return (
        <AuthContext.Provider value={{ user, login, logOut }}>
          {error || loading ? null : children}
        </AuthContext.Provider>
      )
    }
    
    

    受保护的路线.tsx

    const PUBLIC_PATHS = ['/']
    const ADMIN_ROUTES = [...]
    const SUPERVISOR_ROUTES = [...]
    
    
    export function ProtectedRoute({ children }: { children: React.ReactNode }) {
      const { user } = useAuth()
      const router = useRouter()
      const [authorized, setAuthorized] = useState(false)
    
      useEffect(() => {
        console.log(`Current User : ${JSON.stringify(user)}`)
    
        const isAdminRoute = ADMIN_ROUTES.includes(router.pathname)
        const isSupervisorRoute = SUPERVISOR_ROUTES.includes(router.pathname)
    
        // if an token is present send user to
        // authorized route
        if (user !== null) {
          // @ts-ignore
          if (user.role === 1 && isAdminRoute) {
            setAuthorized(true)
            console.log(`Pushing to ${router.pathname}`)
            router.push(router.pathname)
            // @ts-ignore
          } else if (user.role === 2 && isSupervisorRoute) {
            setAuthorized(true)
            console.log(`Pushing to ${router.pathname}`)
            router.push(router.pathname)
          } else {
            console.log(`Invalid role! user: ${JSON.stringify(user)}`)
          }
        } else {
          setAuthorized(false)
          console.log('Sending you to login page')
          // "/" have the login page
          router.push('/')
        }
      }, [user]) // router is avoided from deps to avoid infinite loop
    
      return <>{authorized && children}</>
    }
    
    

    _app.tsx

    export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
      getLayout?: (page: ReactElement) => ReactNode
    }
    
    type AppPropsWithLayout = AppProps & {
      Component: NextPageWithLayout
    }
    
    const noAuthRequired = ['/']
    
    function App({ Component, pageProps }: AppPropsWithLayout) {
      const getLayout = Component.getLayout ?? ((page) => page)
    
      const router = useRouter()
    
      return (
        <ApolloProvider client={CLIENT}>
          <AuthContextProvider>
            {getLayout(
              noAuthRequired.includes(router.pathname) ? (
                <Component {...pageProps} />
              ) : (
                <ProtectedRoute>
                  <Component {...pageProps} />
                </ProtectedRoute>
              ),
            )}
          </AuthContextProvider>
        </ApolloProvider>
      )
    }
    
    export default App
    
    

    当前可能的解决方法是在 ProtectedRoute 以获取用户信息。

    我在那里无论如何都要保存 使用者 对象刷新页面?

    0 回复  |  直到 3 年前