React Admin结合Firestore实现基于权限动态渲染Resource的问题求助
解决方案:正确结合AdminUI与权限控制,避免未登录时渲染资源
我明白你的问题所在:当你把AdminUI放在AsyncResources内部时,未登录状态下AsyncResources会先执行usePermissions,此时permissions为null,导致后续代码崩溃;而把AdminUI放在外层时,又遇到了资源不能被包裹的限制。以下是一个可行的解决方案,既能利用AdminUI的内置认证重定向,又能根据权限动态渲染资源:
核心思路
- 让
AdminUI作为AdminContext的直接子组件,利用它的内置认证逻辑处理未登录重定向。 - 在
AdminUI内部添加一个条件渲染组件,负责处理权限加载、资源动态生成的逻辑,用Fragment包裹多个资源(React Admin允许这种方式,因为Fragment不会生成额外DOM节点)。 - 确保Firestore数据请求仅在用户已认证且权限加载完成后执行,避免未授权调用。
完整代码示例
import { AdminContext, AdminUI, usePermissions, useAuthState } from 'react-admin'; import { useState, useEffect, Fragment } from 'react'; import firebase from 'firebase/app'; import 'firebase/firestore'; // 自定义加载组件、布局、主题等 import Loading from './Loading'; import CustomLayout from './CustomLayout'; import theme from './theme'; import RequestList from './RequestList'; import RequestEdit from './RequestEdit'; import ExpertList from './ExpertList'; import authProvider from './authProvider'; import dataProvider from './dataProvider'; function App() { return ( <AdminContext authProvider={authProvider} dataProvider={dataProvider}> <AdminUI layout={CustomLayout} theme={theme}> <ConditionalResources /> </AdminUI> </AdminContext> ); } function ConditionalResources() { const [emails, setEmails] = useState<string[]>([]); const { loading, permissions } = usePermissions(); const { isAuthenticated } = useAuthState(); // 仅在用户已认证且权限可用时获取Firestore数据 useEffect(() => { const fetchExperts = async () => { if (!isAuthenticated || !permissions) return; const expertsSnapshot = await firebase.firestore().collection("experts").get(); const docIds = expertsSnapshot.docs.map(doc => doc.id); setEmails(docIds); }; fetchExperts(); }, [isAuthenticated, permissions]); // 权限加载中,显示加载状态 if (loading) { return <Loading loadingPrimary="WePractice Admin UI" loadingSecondary="Loading..." />; } // 未登录或权限未就绪,返回null(此时AdminUI会触发登录重定向) if (!isAuthenticated || !permissions) { return null; } // 管理员权限:渲染所有专家请求和专家列表 if (permissions.admin) { return ( <Fragment> {emails.map(email => ( <Resource key={email} options={{ label: email }} name={`experts/${email}/requests`} list={RequestList} edit={RequestEdit} /> ))} <Resource name="experts" list={ExpertList} /> </Fragment> ); } // 普通用户:渲染自己的请求列表 return ( <Resource options={{ label: permissions.email }} name={`experts/${permissions.email}/requests`} list={RequestList} edit={RequestEdit} /> ); } export default App;
关键说明
- 认证流程:
AdminUI会自动调用authProvider的checkAuth方法,未登录时直接重定向到登录页,此时ConditionalResources不会被渲染,避免了permissions为null的崩溃问题。 - 条件渲染:在权限加载完成前,返回加载组件;未登录时返回
null,确保不会触发资源渲染逻辑。 - 资源包裹:用
Fragment(<>简写)包裹多个Resource组件,既满足React的单元素返回要求,又不会被React Admin视为无效包裹(因为Fragment不生成实际DOM节点)。 - Firestore请求时机:将Firestore数据请求的触发条件绑定到
isAuthenticated和permissions,确保只有授权用户才能访问Firestore数据,同时避免未登录时的无效请求。
确保你的authProvider正确实现了checkAuth方法,示例如下(供参考):
const authProvider = { checkAuth: () => { const user = firebase.auth().currentUser; return user ? Promise.resolve() : Promise.reject(); }, // 其他authProvider方法(login, logout, getPermissions等) };
内容的提问来源于stack exchange,提问作者Pietro
相关产品推荐
相关产品推荐

