CASL React上下文报错:Cannot read properties of undefined (reading 'on')
CASL React 上下文实现报错:Cannot read properties of undefined (reading 'on') 解决方法
问题描述
在React上下文提供者中实现CASL React时,持续触发错误:Cannot read properties of undefined (reading 'on')。已定义权限规则,但上下文始终返回undefined,无法定位问题根源,相关代码如下:
原useAbility上下文代码
export const AbilityContext = React.createContext<AnyAbility>({} as AnyAbility); export const Can = createContextualCan(AbilityContext.Consumer) export const defineAbilities = (): any => { const { can, cannot, build } = new AbilityBuilder(createMongoAbility); can('read', 'All'); cannot('delete', 'Extension'); console.log("Abilities Defined"); const ability = build(); return ability }; interface IAbilityProviderProps { children: React.ReactNode; } export const AbilityProvider: React.FC<IAbilityProviderProps> = ({ children }) => { const { ability } = defineAbilities(); return ( <AbilityContext.Provider value={ability} > {children} </AbilityContext.Provider> ) }; export default function useAbilitiesActions() { function setAbilities() { defineAbilities(); }; return { setAbilities } }
原权限渲染按钮代码
const ability = React.useContext(AbilityContext); console.log(ability.can('eat', 'Meat')); return ( <> { data && ( <div className={buttonCellClass}> <Can I="eat" an="meat" passThrough ability={ability} > <IconButton iconProps={deleteIcon} ariaLabel="Delete" text="Delete" onClick={() => { props.onDelete(data); }} disabled={false} //{ability.can('delete', 'Extension')} /> </Can> </div> ) } </> );
原App.tsx中Provider包裹代码
<AbilityProvider> <EventProvider> <Routes> <Route path="/" element={<AssetList />} /> <Route path="/reconciler" element={<Reconciler />} /> {navigationHelper.map(({ id, url, page: Page }) => ( <Route key={id} path={`/${url}`} element={(Page && <Page />) || null} /> ))} </Routes> </EventProvider> </AbilityProvider>
错误原因分析
- 返回值解构错误:
defineAbilities函数直接返回Ability实例,但在AbilityProvider中错误地使用const { ability } = defineAbilities()解构,导致ability变量为undefined。 - Can组件属性名错误:CASL的
Can组件要求使用action和subject属性定义权限,而非自定义的I和an,这会导致组件无法解析权限规则。 - 上下文初始值非法:创建
AbilityContext时用{} as AnyAbility强制转换类型,但空对象不具备Ability实例的核心方法(如can、on),触发undefined调用错误。
修正后的代码
修正后的useAbility上下文代码
import React from 'react'; import { AbilityBuilder, createMongoAbility, AnyAbility } from '@casl/ability'; import { createContextualCan } from '@casl/react'; // 用空的Ability实例作为初始值,避免undefined问题 export const AbilityContext = React.createContext<AnyAbility>(createMongoAbility()); export const Can = createContextualCan(AbilityContext.Consumer); export const defineAbilities = (): AnyAbility => { const { can, cannot, build } = new AbilityBuilder(createMongoAbility); can('read', 'All'); cannot('delete', 'Extension'); console.log("Abilities Defined"); return build(); }; interface IAbilityProviderProps { children: React.ReactNode; } export const AbilityProvider: React.FC<IAbilityProviderProps> = ({ children }) => { // 直接接收返回的Ability实例,无需解构 const ability = defineAbilities(); return ( <AbilityContext.Provider value={ability}> {children} </AbilityContext.Provider> ); }; export default function useAbilitiesActions() { function setAbilities() { return defineAbilities(); }; return { setAbilities }; }
修正后的权限渲染按钮代码
const ability = React.useContext(AbilityContext); console.log(ability.can('eat', 'Meat')); return ( <> {data && ( <div className={buttonCellClass}> <Can action="eat" subject="Meat" passThrough ability={ability} > <IconButton iconProps={deleteIcon} ariaLabel="Delete" text="Delete" onClick={() => { props.onDelete(data); }} disabled={!ability.can('delete', 'Extension')} /> </Can> </div> )} </> );
说明
修正后,上下文会正确传递合法的Ability实例,Can组件能正常解析权限规则,undefined相关错误将被解决。同时确保所有使用权限的组件都处于AbilityProvider的包裹范围内。
内容的提问来源于stack exchange,提问作者Conrad Addo
相关产品推荐
相关产品推荐

