@daveyplate/ 更好的- auth- tanstack (中文(简体) )
Tanstack 查询钩为更好的奥特。 更严格地控制请求何时发出,乐观地突变,并通过Persist Cients支持离线缓存以获取离线授权.
更多即将到来... (例如更多的SSR预选,组织与条纹插件钩子,SWR端口. )
☕️ 给我买杯咖啡
先决条件
首先,你需要安装和整合 更好的认证 打开 Tanstack 查询。 。 。 .
安装
小时
pnpm add @daveyplate/better-auth-tanstack@latest
无
npm install @daveyplate/better-auth-tanstack@latest
给 useSession 钩子可以刷新签入、签出和签入时无需电子邮件验证,您必须手动拨打 refetch 或者说 queryClient.resetQueries() 页:1 ["session"] 输入 onSuccess 在等待和检查出错后召回其中每个函数.
如果您正在使用 Next.js App 路由器和受保护的中间软件路线, router.refresh() 还需要清除路由器缓存.
@daveyplate/ 更好 - auth - ii 更好的Auth UI支持更好的Auth-tanstack出盒子! 你只需要用这个 AuthUIProviderTanstack。 。 。 .
建立认证查询程序
首先,你需要设置 AuthQueryProvider 在您的申请。 此提供者将为钩子的功能提供必要的上下文. 要求数 "use client" next.js App路由器指令 .
应用程序/提供者.tsx
"use client"
import { AuthQueryProvider } from "@daveyplate/better-auth-tanstack"
export default function Providers({
children,
}: {
children: React.ReactNode
}) {
return (
<AuthQueryProvider>
{children}
</AuthQueryProvider>
)
}
应用程序/layout.tsx
import { Providers } from "./providers"
export default function RootLayout({
children,
}: {
children: React.ReactNode
}) {
return (
<Providers>
{children}
</Providers>
)
}
AuthQuery 提供支持
这个 AuthQueryProvider 组件接受以下道具。 默认 staleTime 页:1 useSession 60秒时 useToken 10分钟.
| Prop | 类型 | 说明 |
|---|---|---|
| 查询选项 ? | 使用查询选项 | 提供者的可选查询选项 . |
| 会话查询选项? | 使用查询选项 | 会话查询的可选查询选项. |
| 符号查询选项 ? | 使用查询选项 | 符号查询的可选查询选项 . |
| 会场钥匙? | 字符串 [] | 会话查询的可选密钥 。 默认是 ["session"]。 。 。 . |
| 钥匙? | 字符串 [] | 符号查询的可选密钥 。 默认是 ["token"]。 。 。 . |
| 列表账户键 ? | 字符串 [] | 列表账户查询的可选密钥 。 默认是 ["list-accounts"]。 。 。 . |
| 会话列表 Key? | 字符串 [] | 列表会议查询的可选密钥 。 默认是 ["list-sessions"]。 。 。 . |
| 乐观? | 布尔 | 是否执行乐观更新 。 默认是 true。 。 。 . |
| 电磁? | 布尔 | 变异后是否进行回溯 。 默认是 true。 。 。 . |
创建 auth-hooks.ts
创建名为的文件 auth-hooks.ts 并使用 createAuthHooks 函数。 此函数取 authClient 实例,然后以完整的类型安全返回钩子,并从您的 authClient。 。 。 .
import { createAuthHooks } from "@daveyplate/better-auth-tanstack"
import { authClient } from "@/lib/auth-client"
export const authHooks = createAuthHooks(authClient)
export const {
useSession,
usePrefetchSession,
useToken,
useListAccounts,
useListSessions,
useListDeviceSessions,
useListPasskeys,
useUpdateUser,
useUnlinkAccount,
useRevokeOtherSessions,
useRevokeSession,
useRevokeSessions,
useSetActiveSession,
useRevokeDeviceSession,
useDeletePasskey,
useAuthQuery,
useAuthMutation
} = authHooks
用钩子
使用会议
这个 useSession 钩子用来获取会话 .
道具
| Prop | 类型 | 说明 |
|---|---|---|
| 选项? | 使用查询选项 | 会话查询的可选查询选项. |
示例
import { useSession } from "@/hooks/auth-hooks"
function MyComponent() {
const {
data: sessionData,
session,
user,
isPending,
refetch,
error
} = useSession()
if (isPending) return <div>Loading...</div>
return <div>Welcome, {user?.email}</div>
}
使用脚本
这个 useToken 如果启用了更好的- 自动 JWT 插件, 钩子可以获取 JWT 令牌 .
道具
| Prop | 类型 | 说明 |
|---|---|---|
| 选项? | 使用查询选项 | 符号查询的可选查询选项 . |
示例
import { useToken } from "@/hooks/auth-hooks"
function MyComponent() {
const { data, token, payload, isPending, error } = useToken()
if (isPending) return <div>Loading...</div>
return <div>JWT: {token}</div>
}
使用列表账户
这个 useListAccounts 勾通允许您列出和管理链接到不同提供者的用户账户.
使用量
import { useListAccounts } from "@/hooks/auth-hooks"
function AccountList() {
const { data: accounts, isPending, error } = useListAccounts()
if (isPending) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>
return (
<div>
<h2>Linked Accounts</h2>
<ul>
{accounts?.map(account => (
<li key={account.id}>
{account.provider}
<button onClick={() => unlinkAccount(account.provider)}>Unlink</button>
</li>
))}
</ul>
</div>
)
}
使用 unlinkAccount 函数以解开一个账户的提供者ID。 这是乐观的例子。 见下文非乐观的例子.
unlinkAccount({ providerId: "github" })
使用列表会议
import { useListSessions, useRevokeSession } from "@/hooks/auth-hooks"
function SessionList() {
const {
data: sessions,
isPending,
error
} = useListSessions()
const { mutate: revokeSession } = useRevokeSession()
if (isPending) return <div>Loading...</div>;
if (error) return <div>Error: {error.message}</div>
return (
<div>
<h2>Active Sessions</h2>
<ul>
{sessions?.map(session => (
<li key={session.id}>
{session.userAgent}
<button onClick={() => revokeSession(session.token)}>Revoke</button>
</li>
))}
</ul>
</div>
)
}
使用列表
const {
data: deviceSessions,
isPending,
error
} = useListDeviceSessions()
使用列表Passkeys
const {
data: passkeys,
isPending,
error
} = useListPasskeys()
变异 - 更新用户
乐观范例
无加载器更新用户名的优化示例. 优化更新 Tanstack Query 缓存中的用户 。 对成功进行修正,对错误进行恢复。 使用默认设置用于 optimistic (真实) 道具在 AuthQueryProvider。 。 。 .
此软件包支持 Tanstack 查询全局错误配置 :
queryClient.getQueryCache().config.onError 自动调用, 这样您就可以设置全局错误托盘来显示 error.message || error.statusText。 。 。 . Tanstack 查询全局错误调用
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useSession, useUpdateUser } from "@/hooks/auth-hooks"
export default function SettingsPage() {
const { user, isPending } = useSession()
const { mutate: updateUser, error: updateError } = useUpdateUser()
const [disabled, setDisabled] = useState(true)
const updateProfile = (formData: FormData) => {
const name = formData.get("name") as string
setDisabled(true)
updateUser({ name: name })
}
useEffect(() => {
if (updateError) {
// Show an error Toast
}
}, [updateError])
if (isPending || !user) {
return (
<div>
Loading...
</div>
)
}
return (
<div className="flex flex-col items-center gap-4 my-auto">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>
Change Name
</CardTitle>
</CardHeader>
<CardContent>
<form
action={updateProfile}
className="flex flex-col gap-4 items-start"
>
<Label htmlFor="name">
Name
</Label>
<Input
defaultValue={user.name}
name="name"
placeholder="Name"
onChange={() => setDisabled(false)}
/>
<Button disabled={disabled}>
Save
</Button>
</form>
</CardContent>
</Card>
</div>
)
}
不理想的例子
不理想的例子 useActionState 以显示更新用户名的加载器。 设定 optimistic 改为 false 输入 AuthQueryProvider 禁用乐观缓存更新的道具 。 发送请求到 /api/auth/update-user 然后在请求成功后更新 Tanstack 查询缓存中的用户。 然后通过调试重新验证会话.
注意我们使用 updateUserAsync
"use client"
import { Loader2 } from "lucide-react"
import { useActionState } from "react"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { useSession, useUpdateUser } from "@/hooks/auth-hooks"
import { cn } from "@/lib/utils"
export default function SettingsPage() {
const { user, isPending } = useSession()
const { mutate: updateUser, error: updateError } = useUpdateUser()
const [disabled, setDisabled] = useState(true)
type ActionState = Parameters<typeof updateUser>[0]
const updateProfile = async (_: ActionState, formData: FormData) => {
const name = formData.get("name") as string
setDisabled(true)
const { error } = await updateUserAsync({ name, fetchOptions: { throw: false } })
if (error) {
// Show an error Toast or throw an error to an ErrorBoundary
setDisabled(false)
}
return { name } as ActionState
}
const [state, action, isSubmitting] = useActionState(updateProfile, {})
// useEffect(() => {
// if (updateError) {
// setDisabled(false)
// // Show an error Toast
// }
// }, [updateError])
if (isPending || !user) {
return (
<div>
Loading...
</div>
)
}
return (
<div className="flex flex-col items-center gap-4 my-auto">
<Card className="w-full max-w-md">
<CardHeader>
<CardTitle>
Change Name
</CardTitle>
</CardHeader>
<CardContent>
<form
action={action}
className="flex flex-col gap-4 items-start"
>
<Label htmlFor="name">
Name
</Label>
<Input
defaultValue={state?.name ?? user.name}
name="name"
placeholder="Name"
onChange={() => setDisabled(false)}
/>
<Button disabled={isSubmitting || disabled}>
<span className={cn(isSubmitting && "opacity-0")}>
Save
</span>
{isSubmitting && <Loader2 className="animate-spin absolute" />}
</Button>
</form>
</CardContent>
</Card>
</div>
)
}
服务器- Side 预选 - 高级使用
如果您想要使用混合预选策略, 则完全支持此策略 .
SSR 会前会议
这个 prefetchSession 函数在服务器中用于预取会话数据并存储在查询客户端中.
道具
| Prop | 类型 | 说明 |
|---|---|---|
| 认证 | 认证 | 服务器认证实例 . |
| 查询客户端 | 查询客户端 | 查询客户端实例 . |
| 页眉 | 页眉 | 信头对服务器请求有异议 . |
| 查询 Key ? | 字符串 [] | 会话查询的可选密钥 。 默认是 ["session"]。 。 。 . |
RSC 示例
import { prefetchSession } from "@daveyplate/better-auth-tanstack/server"
import { HydrationBoundary, QueryClient, dehydrate } from "@tanstack/react-query"
import { headers } from "next/headers"
import { betterAuth } from "better-auth"
import { auth } from "@/lib/auth"
export default async function Page() {
const queryClient = new QueryClient()
const { data, session, user } = await prefetchSession(
auth, queryClient, await headers()
)
return (
<HydrationBoundary state={dehydrate(queryClient)}>
<ClientPage />
</HydrationBoundary>
)
}
使用认证查询
import type { AnyUseQueryOptions } from "@tanstack/react-query"
import { useAuthQuery } from "@/lib/auth-hooks"
import { authClient } from "@/lib/auth-client"
export function useListDeviceSessions(
options?: Partial<AnyUseQueryOptions>
) {
return useAuthQuery({
queryKey: ["device-sessions"],
queryFn: authClient.multiSession.listDeviceSessions,
options
})
}
认证混合
import { AuthQueryContext, type AuthQueryOptions } from "@daveyplate/better-auth-tanstack"
import { authClient } from "@/lib/auth-client"
import { useAuthMutation } from "@/lib/auth-hooks"
export function useUpdateUser(
options?: Partial<AuthQueryOptions>
) {
type SessionData = typeof authClient["$Infer"]["Session"]
const { sessionKey: queryKey } = useContext(AuthQueryContext)
return useAuthMutation({
queryKey,
mutationFn: authClient.updateUser,
optimisticData: (params, previousSession: SessionData) => ({
...previousSession,
user: { ...previousSession.user, ...params }
}),
options
})
}
许可证
这个项目根据麻省理工学院的许可证获得许可。 详情见LICENSE文件.