React-Admin usePermissions钩子授权后role未定义问题排查
权限获取异常问题排查
问题描述
我尝试通过usePermissions钩子获取角色来控制UI资源的访问权限,但授权完成后permissions.role的值始终是undefined,只有刷新浏览器页面后才能拿到预期值,请问问题出在哪?
相关代码
App组件代码
import * as React from "react"; // ... 其他导入 const store = localStorageStore(); function App() { return ( <AdminContext authProvider={authProvider} dataProvider={CabDataProvider} i18nProvider={i18nProvider} store={store} > <AsyncResources /> </AdminContext> ); } interface CabDataProvider extends DataProvider { getResources: () => Promise<{ name: string }[]>; } function AsyncResources() { const [resources, setResources] = useState<Array<{ name: string }>>( [] as Array<{ name: string }> ); const dataProvider = useDataProvider<CabDataProvider>(); useEffect(() => { dataProvider.getResources().then((r) => setResources(r)); }, []); const { isLoading, permissions } = usePermissions(); if (isLoading == false) { console.log(permissions); return ( <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}> {permissions.role === "admin" ? ( <Resource name="users" create={UserCreate} list={UsersList} edit={UserEdit} /> ) : null} <Resource name="offices" list={OfficesList} /> <CustomRoutes noLayout> <Route path="/registration" element={<CabRegistration />} /> </CustomRoutes> </AdminUI> ); } else { return ( <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}> </AdminUI> ) } } export default App;
authProvider中的getPermissions方法
getPermissions: () => { try { const role = localStorage.getItem("role" as string)?.replace(/"/g, ""), permissions = localStorage.getItem("permissions" as string)?.replace(/"/g, ""); return Promise.resolve({ role, permissions }); } catch (error) { return Promise.reject({ message: "Failed to retrieve permissions", error }); } }
原因分析
- 执行时机不匹配:
usePermissions在组件挂载时就会调用authProvider.getPermissions(),但此时登录流程刚完成,localStorage里的role和permissions还没被写入,所以拿到的是undefined。 - 无监听机制:
usePermissions默认不会监听localStorage的变化,所以登录后写入的数据无法触发权限的重新获取,只有刷新页面时,组件重新挂载才会读取到最新的localStorage值。
解决方案
1. 确保登录后正确写入localStorage
在authProvider的login方法中,必须在登录接口返回成功后,再将role和permissions写入localStorage,然后再resolve Promise,确保react-admin感知到登录完成:
login: async (credentials) => { // 调用登录接口获取用户信息 const res = await yourLoginApi(credentials); // 写入localStorage(建议用JSON.stringify处理,避免格式问题) localStorage.setItem('role', JSON.stringify(res.role)); localStorage.setItem('permissions', JSON.stringify(res.permissions)); // 通知react-admin登录成功 return Promise.resolve(); }
2. 让usePermissions感知数据变化
利用usePermissions返回的refetch方法,监听localStorage的变化,当role或permissions更新时,触发权限重新获取:
function AsyncResources() { const [resources, setResources] = useState<Array<{ name: string }>>([]); const dataProvider = useDataProvider<CabDataProvider>(); // 解构出refetch方法 const { isLoading, permissions, refetch } = usePermissions(); useEffect(() => { dataProvider.getResources().then((r) => setResources(r)); }, []); // 添加localStorage监听 useEffect(() => { const handleStorageUpdate = (e: StorageEvent) => { if (e.key === 'role' || e.key === 'permissions') { refetch(); } }; window.addEventListener('storage', handleStorageUpdate); // 组件卸载时移除监听 return () => window.removeEventListener('storage', handleStorageUpdate); }, [refetch]); if (!isLoading) { console.log(permissions); return ( <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}> // 可选:添加可选链避免undefined报错 {permissions?.role === "admin" && ( <Resource name="users" create={UserCreate} list={UsersList} edit={UserEdit} /> )} <Resource name="offices" list={OfficesList} /> <CustomRoutes noLayout> <Route path="/registration" element={<CabRegistration />} /> </CustomRoutes> </AdminUI> ); } else { return ( <AdminUI ready={Loading} loginPage={CabLogin} dashboard={Dashboard}> </AdminUI> ) } }
3. 优化getPermissions的取值逻辑
用JSON.parse代替字符串替换,避免格式错误导致的取值异常:
getPermissions: () => { try { const roleStr = localStorage.getItem("role"); const permissionsStr = localStorage.getItem("permissions"); // 解析JSON字符串,避免引号替换的脆弱性 const role = roleStr ? JSON.parse(roleStr) : null; const permissions = permissionsStr ? JSON.parse(permissionsStr) : null; return Promise.resolve({ role, permissions }); } catch (error) { return Promise.reject({ message: "Failed to retrieve permissions", error }); } }
内容的提问来源于stack exchange,提问作者1vanguard
相关产品推荐
相关产品推荐

