ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Refine 教程实战:实现 authProvider 的 login/logout 方法与 useLogin/useLogout 钩子

2026/9/13 7:25:29 拓冰建站 浏览量
Refine 教程实战:实现 authProvider 的 login/logout 方法与 useLogin/useLogout 钩子 Refine 教程实战实现 authProvider 的 login/logout 方法与 useLogin/useLogout 钩子【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine本文基于 Refine 官方教程 Logging In Out 展开讲解如何在 Refine 应用中完整实现用户登录与登出从在authProvider中实现login、logout方法到使用useLogin、useLogout钩子构建登录页与登出按钮。读完本文你不仅能按步骤复现整个登录/登出流程还能理解其底层基于 TanStack Query 的 mutation 机制、登录成功后的重定向逻辑以及check方法的自动失效原理。前置背景保护内容与实现认证的动作教程的上下文是上一步已经在src/App.tsx中通过Authenticated /组件保护了页面内容未登录用户会被挡在外面。本篇要解决的核心问题是用户如何进入login和退出logout系统。整体涉及四个文件src/providers/auth-provider.ts实现login/logout方法src/pages/login.tsx新建登录组件使用useLogin钩子src/components/header.tsx新建头部组件使用useLogout钩子src/App.tsx把Login /挂载为Authenticated /的fallback把Header /挂载为其 children。示例使用 Refine 官方提供的 Fake REST APIhttps://api.fake-rest.refine.dev测试账号为demodemo.com/demodemo。实现 login 方法login方法负责用户认证及相关操作例如存储 token。按 Refine 的约定它需要返回一个Promiseresolve 的对象中必须包含success属性用来表明登录操作是否成功。Fake REST API 要求向/auth/login端点发送POST请求请求体中带上email和password参数响应体会返回token。登录成功后我们会把token存入localStorage以便后续使用。更新src/providers/auth-provider.ts加入login方法import { AuthProvider } from refinedev/core; export const authProvider: AuthProvider { // login 方法会接收一个对象包含你传给 useLogin 钩子的全部值 login: async ({ email, password }) { const response await fetch( https://api.fake-rest.refine.dev/auth/login, { method: POST, body: JSON.stringify({ email, password }), headers: { Content-Type: application/json, }, }, ); const data await response.json(); if (data.token) { localStorage.setItem(my_access_token, data.token); return { success: true }; } return { success: false }; }, check: async () { const token localStorage.getItem(my_access_token); return { authenticated: Boolean(token) }; }, logout: async () { throw new Error(Not implemented); }, onError: async (error) { throw new Error(Not implemented); }, // ... };源码视角login 方法的响应类型在 core 包的 认证类型定义 中login的签名是login: (params: any) PromiseAuthActionResponse;而AuthActionResponse的完整结构比教程示例用到的更多export type AuthActionResponse { success: boolean; // 必填操作是否成功 redirectTo?: string; // 成功后要跳转的路径 error?: RefineError | Error; // 携带错误信息 [key: string]: unknown; successNotification?: SuccessNotificationResponse; // 可选的成功通知 };也就是说除了教程演示的{ success: true }之外login还可以返回redirectTo登录后跳转地址、error错误对象和successNotification自定义成功提示这些字段都会被上层钩子消费下文会展开。使用 useLogin 钩子login方法实现之后就可以在组件中调用useLogin钩子完成登录了。按教程先创建一个Login组件然后把它作为fallback传给src/App.tsx中的Authenticated /组件。更新src/App.tsximport { Refine, Authenticated } from refinedev/core; import { dataProvider } from ./providers/data-provider; import { authProvider } from ./providers/auth-provider; import { ShowProduct } from ./pages/products/show; import { EditProduct } from ./pages/products/edit; import { ListProducts } from ./pages/products/list; import { CreateProduct } from ./pages/products/create; import { Login } from ./pages/login; export default function App(): JSX.Element { return ( Refine dataProvider{dataProvider} authProvider{authProvider} Authenticated keyprotected fallback{Login /} {/* ShowProduct / */} {/* EditProduct / */} ListProducts / {/* CreateProduct / */} /Authenticated /Refine ); }注意key属性从 Authenticated 组件源码 的 JSDoc 可以看到当同一层级存在多个Authenticated实例时key是必需的——它强制 React 在认证状态变化时卸载并重新挂载组件而不是仅仅更新 props从而避免fallback与children错误渲染的问题。最后在src/pages/login.tsx中引入useLogin并调用它import React from react; import { useLogin } from refinedev/core; export const Login () { const { mutate, mutation: { isPending }, } useLogin(); const onSubmit (event: React.FormEventHTMLFormElement) { event.preventDefault(); // 使用 FormData 收集表单值并转换为对象 const data Object.fromEntries(new FormData(event.target).entries()); // 调用 mutate提交从表单收集到的数据 mutate(data); }; return ( div h1Login/h1 form onSubmit{onSubmit} label htmlForemailEmail/label input typeemail idemail nameemail // 为方便演示提供默认值 defaultValuedemodemo.com / label htmlForpasswordPassword/label input typepassword idpassword namepassword // 为方便演示提供默认值 defaultValuedemodemo / {isPending spanloading.../span} button typesubmit disabled{isPending} Submit /button /form /div ); };源码视角useLogin 内部到底做了什么useLogin的完整实现在 useLogin/index.ts。它本质上是一个对 TanStack QueryuseMutation的封装mutationFn直接取自 auth context 中你注册的login方法。关键行为有以下几点mutationKey使用keys().auth().action(login).get()生成这是后续失效check查询的基础见文末。成功后的重定向onSuccess中先读取路由 URL 查询参数里的touseParsed()解析得到其次读取login返回的redirectTo若存在则执行go({ to, type: replace })跳转。这与 useLogin 的单元测试 中的断言一致返回{ success: true, redirectTo: / }时测试期望mockGo被调用且参数为{ to: /, type: replace }而只返回{ success: true }时则断言go未被调用。通知机制登录失败error或success: false时自动弹出 key 为login-error的错误通知默认文案为 Invalid credentials若login返回了successNotification则弹出对应的成功通知。组件本身无需手写任何提示逻辑。认证状态失效成功后延迟 32ms 调用invalidateAuthStore()重新触发check见下文登录登出后为何页面会自动切换。可定制的 mutationOptionsuseLogin({ mutationOptions })支持传入除mutationFn之外的任意UseMutationOptions例如自定义onSuccess/retry等且 hook 内置的meta会与其合并而非覆盖。另外从源码结构看useLogin还接入了getXRay(useLogin)来自refinedev/devtools-internal为 DevTools 的调用追踪提供了元信息这属于框架内部能力业务代码无需关心。实现 logout 方法logout方法负责登出用户及相关操作例如移除 token。它同样需要返回一个Promiseresolve 的对象中包含success属性以表明登出是否成功。Fake REST API 不需要发送任何请求来登出用户我们只需从localStorage中删除tokenimport { AuthProvider } from refinedev/core; export const authProvider: AuthProvider { logout: async () { localStorage.removeItem(my_access_token); // 返回 success: true 表明登出操作成功 return { success: true }; }, // login method receives an object with all the values youve provided to the useLogin hook. login: async ({ email, password }) { // 与上文 login 实现一致略 }, check: async () { const token localStorage.getItem(my_access_token); return { authenticated: Boolean(token) }; }, onError: async (error) { throw new Error(Not implemented); }, // ... };使用 useLogout 钩子logout方法实现之后就可以调用useLogout钩子了。教程的做法是创建一个Header组件里面放一个登出按钮并把它挂载到Refine /组件内部即Authenticated /的 children 位置。更新src/App.tsximport { Refine, Authenticated } from refinedev/core; import { dataProvider } from ./providers/data-provider; import { authProvider } from ./providers/auth-provider; import { ShowProduct } from ./pages/products/show; import { EditProduct } from ./pages/products/edit; import { ListProducts } from ./pages/products/list; import { CreateProduct } from ./pages/products/create; import { Login } from ./pages/login; import { Header } from ./components/header; export default function App(): JSX.Element { return ( Refine dataProvider{dataProvider} authProvider{authProvider} Authenticated keyprotected fallback{Login /} Header / {/* ShowProduct / */} {/* EditProduct / */} ListProducts / {/* CreateProduct / */} /Authenticated /Refine ); }更新src/components/header.tsximport React from react; import { useLogout } from refinedev/core; export const Header () { const { mutate, mutation: { isPending }, } useLogout(); return ( h2Welcome!/h2 button typebutton disabled{isPending} onClick{mutate} Logout /button / ); };源码视角useLogout 的重定向规则比 login 更细致useLogout的实现位于 useLogout/index.ts。与useLogin类似它也是useMutation封装mutationKey 为keys().auth().action(logout).get()但重定向逻辑略有不同useLogout额外支持在调用mutate时传入variables其中的redirectPath?: string | false可以覆盖响应中的redirectTo源码中const redirect redirectPath ?? redirectTo;当redirect false时完全不跳转适合登出但不离开当前页的场景这与 types.ts 顶部注释中描述的apps can exist ... do not redirect after logout设计意图相符登出成功且需要跳转时调用go({ to: redirect })注意这里是普通push而非replace随后立即调用invalidateAuthStore()。登录登出后为何页面会自动切换教程最后提到登录成功后Authenticated /组件会渲染 children 而非fallback登出时同理。Refine 会自动处理check方法的失效开发者无需关心。这个自动背后的机制在源码中清晰可见useLogin和useLogout都依赖 useInvalidateAuthStoreconst invalidate async () { await Promise.all( ([check, identity, permissions] as const).map((action) queryClient.invalidateQueries({ queryKey: keys().auth().action(action).get(), }), ), ); };它会一次性失效check、identity、permissions三个查询的缓存触发authProvider.check重新执行。而Authenticated /组件内部通过useIsAuthenticated订阅了check的结果组件源码 中的渲染优先级为isFetching正在校验→ 渲染loading若设置了已认证 → 渲染children未认证且设置了fallback→ 渲染fallback即本教程中的Login /未认证且无fallback→ 重定向到redirectOnFail或check返回的redirectTo。因此整个闭环是mutate调用 provider 方法 → provider 修改 localStorage → 钩子失效check查询 →check重新读取 token →Authenticated /依据最新认证状态在fallback与children之间切换。另外值得一提的是Authenticated /的appendCurrentPathToQuery能力默认开启当走重定向分支时它会把当前路径附加为跳转 URL 查询参数中的to而useLogin的onSuccess会优先读取这个to参数作为登录成功后的跳转目标——这两段代码合起来实现了访问受保护页面 → 被重定向到登录页 → 登录后回到原页面的完整体验。小结与下一步至此Refine 应用中基于 Fake REST API 的登录/登出流程已经完整跑通authProvider.loginPOST 请求换 token写入localStorage返回{ success }authProvider.logout清除localStorage中的 token返回{ success }useLogin/useLogout基于 TanStack Query mutation 的钩子封装附带错误通知、重定向与认证缓存失效Authenticated /依据check结果在fallback登录页与受保护内容之间切换。教程在此之后会进入下一节用户身份user identity的使用。若希望进一步了解login返回值的redirectTo、successNotification等高级用法可直接阅读仓库中 useLogin 源码 与 useLogout 源码 的实现细节。【免费下载链接】refineA React Framework for building internal tools, admin panels, dashboards B2B apps with unmatched flexibility.项目地址: https://gitcode.com/GitHub_Trending/re/refine创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考