I’m implementing CASL React in a context provider. I keep getting the error: Cannot read properties of undefined (reading ‘on’)
I define the abilities, but kepp gettingundefined in my context.
Am I defining the values correctly and should I be adding something to the context or use a different way? I can’t understand wh it is undefined.
Here is my useAbility context:
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 }
}
Here I have a button that should render the button if the user has permissions:
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);
}}
//typeof props.disabled === 'function' ? props.disabled(data) :
disabled={false} //{ability.can('delete', 'Extension')}
/>
</Can>
</div>
)
}
</>
);
In my App.tsx I pass AbilityProvider to the app like this:
<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>
3
I was trying to create another instance unknowingly of ability. I ended up exporting ability whcih is created inside a CASL context.
Here:
import { defineAbility } from '@casl/ability';
const ability = defineAbility((can, cannot) => {
can('manage', 'all');
cannot('delete', 'Covenant');
can('read', 'All');
can('delete', 'Extension');
});
ability.can('read', 'All'); // true
ability.can('delete', 'Extension'); // false
export default ability
And then inside Can.tsx:
import { createContext } from 'react';
import { createContextualCan } from '@casl/react';
import { AnyAbility } from '@casl/ability';
export const AbilityContext = createContext<AnyAbility>({} as AnyAbility);
export const Can = createContextualCan(AbilityContext.Consumer)