next.js 检查下一个13中的isAdmin在客户端与客户端组件?

h7wcgrx3  于 2023-10-18  发布在  其他
关注(0)|答案(1)|浏览(111)

我正在使用Lucia Auth进行身份验证。
此函数返回session对象:

app/auth/lucia.ts

export const getPageSession = cache(async () => {
    const authRequest = auth.handleRequest("GET", context)
    const session = await authRequest.validate()

    return session
})

app/dashboard/admin.tsx

"use client"

import React from "react"
import { redirect } from "next/navigation"
import NextImg from "next/image"
import ky, { KyResponse } from "ky"

import { getPageSession } from "@/app/auth/lucia"
import { BASE_URL } from "@/app/lib/constants"

const Admin = async () => {
    const [admin, setAdmin] = React.useState(false)

    React.useEffect(() => {
        const fetchAdmin = async () => {
            const res: KyResponse & { isAdmin: boolean } = await ky.get(`${BASE_URL}/api/admin`).json()
            setAdmin(res.isAdmin)
        }
        fetchAdmin()
    }, [])

    const session = await getPageSession()

    if (!session && !admin) redirect("/login")

    return (
        <div className="isolate bg-slate-900 text-white">
            ...
        </div>
    )
}

export default Admin

第一个检查是检查session是否存在,因为只有登录的用户才能访问该页面。它完全工作正常。
第二个检查是检查它是否是一个admin,因为只有管理员可以访问该页面。这不管用
我得到这个错误:
您正在导入一个需要next/header的组件。这只能在服务器组件中工作,但它的父组件之一标记为“使用客户端”,因此它是客户端组件。
getPageSession是服务器端检查,React.useEffect是对API的客户端调用,它返回带有布尔值(如{ isAdmin: false }{ isAdmin: true })的JSON
如何执行这两个检查而不出错?我得到了太多关于在客户端组件中使用服务器的错误。
理想情况下,我想要一个名为isAdmin的通用函数,它会返回某人是否是管理员。如果没有,那就重定向。如果是,则显示页面。
我该怎么做?

a14dhokn

a14dhokn1#

我找到了解决办法。不知道这是否是最好的方法,但它在这里。
我创建了一个客户端组件来检查某人是否是管理员:

components/dashboard/AdminComponent.tsx

此解决方案不起作用。我敢打赌,我使用useEffect的错误,不知何故,可以修复,但下面的替代解决方案工作正常。

"use client"

import React from "react"
import { redirect } from "next/navigation"
import ky, { KyResponse } from "ky"

import { BASE_URL } from "@/app/lib/constants"

export const AdminComponent = () => {
    const [admin, setAdmin] = React.useState(false)

    React.useEffect(() => {
        const fetchAdmin = async () => {
            const res: KyResponse & { isAdmin: boolean } = await ky.get(`${BASE_URL}/api/admin`).json()
            setAdmin(res.isAdmin)
        }
        fetchAdmin()
    }, [])

    if (!admin) redirect("/dashboard")

    return null
}

然后,我将客户端组件导入到服务器组件中。

pages/admin.tsx

import React from "react"
import { redirect } from "next/navigation"

import { getPageSession } from "@/app/auth/lucia"
import { AdminComponent } from "@/app/components/dashboard/AdminComponent"

const Admin = async () => {
    const session = await getPageSession()

    if (!session) redirect("/login")

    return (
        <div className="isolate bg-slate-900 text-white">
            ...
        </div>
    )
}

export default Admin

我还将AdminComponent重写为:

"use client"

import React from "react"
import { redirect } from "next/navigation"
import ky, { KyResponse } from "ky"

import { BASE_URL } from "@/app/lib/constants"

const fetchAdmin = React.cache(async () => {
    const res: KyResponse & { isAdmin: boolean } = await ky.get(`${BASE_URL}/api/admin`).json()
    return res.isAdmin
})

export const AdminComponent = () => {
    const admin = React.use(fetchAdmin())

    if (!admin) redirect("/dashboard")

    return null
}

这看起来干净得多,但到目前为止还不是一个常见的模式。
想知道是否有更简单或更干净的解决方案。

编辑:

找到了更好的解决办法。

components/dashboard/AdminComponent.tsx

"use client"

import React from "react"
import { redirect } from "next/navigation"

import { useAdminStatus } from "@/app/hooks/useAdminStatus"

// eslint-disable-next-line react/display-name
export const AdminComponent = React.memo(() => {
    const { data, error, isLoading } = useAdminStatus()

    if (isLoading) return null
    if (error) return <span className="text-slate-500">Failed to load `AdminComponent`</span>

    if (!data.isAdmin) redirect("/dashboard")

    return null
})

hooks/useAdminStatus.tsx

import useSWRImmutable from "swr/immutable"

const fetcher = (...args: [any]) => fetch(...args).then((res) => res.json())

export const useAdminStatus = () => {
    const { data, error, isLoading } = useSWRImmutable(`/api/admin`, fetcher)

    return {
        data,
        error,
        isLoading,
    }
}

相关问题