New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Race condition in unsubcription of useEffect in useAbility #535
Comments
Thanks for the issue! Race condition cannot be in Can you please remove |
I tried again without my unsubscribe fix with and without dependency array. I could reproduce the issue consistently. I have changed the code now like this:
This will consistently output |
Thank you for investigation! Would be awesome if you could setup a example that is similar to your app. Just to reproduce it in isolation because otherwise I will wander in the possible scenarios. I will a take closer look at |
I have another strange bug in my application which I think is linked to this. |
Btw, I am using useAbility only with a single global AbilityContext. I am also sure ability.update(rules) is executed, but many ability.on('updated') event handlers suddenly stop working |
I made another test. |
Could it be that unlinkItem is the problem? It seems that an unlinkItem on the head element will truncate the list to the single head item. |
if you would provide me a working isolated test, I could take a closer look quicker. The issue may be related to the fact that list is modified during
It seems that all this happens during execution of a single updated event handler. |
Meanwhile I am pretty sure the last line of LinkedItem.ts is the culprit:
What happens if item is the head element of the list? The problem is that _events.get(event) keeps a second reference on this item. |
I commented this line now (its not strictly necessary for the correct linked list implementation I think) and everything works as expected. |
Built-in Nodejs emitter works exactly the same way, as casl's one: const { EventEmitter } = require("events");
const e = new EventEmitter();
const handlers = [() => console.log(1), () => console.log(2), () => console.log(3),() => console.log(4)]
e.on("event", () => {
handlers[0]()
e.off("event", handlers[2]);
});
e.on("event", handlers[1])
e.on("event", handlers[2])
e.on("event", handlers[3])
e.emit('event') // calls all 4 handlers, despite the fact that 2nd was removed during emit
e.emit('event') // calls 3 handlers So, the issue is not in the emitter. the issue is in So, the proper fix would be to add a check that component is unmounted and not call logic inside (what you showed in the very first fix) |
Actually, I found an issue in emitting events and fixed in @casl/ability-5.4.1, could you please test it on your project and report back whether it fixes the issue or react hook also needs to be adapted? |
import { defineAbility } from '@casl/ability';
const ability = defineAbility((can, cannot) => {
can('manage', 'all');
cannot('delete', 'User');
});
ability.on('updated', event => {
console.log('1');
});
ability.on('updated', event => {
console.log('2');
});
ability.on('updated', event => {
console.log('3');
});
ability.on('updated', event => {
console.log('4');
});
const unsubscribeHead = ability.on('updated', event => {
console.log('5');
});
ability.update(ability.rules);
unsubscribeHead();
ability.update(ability.rules); Output is:
After unsubscribing the head element the other event handlers are lost If you unsubscribe any other elements except the head its working |
Thank you for identifying this case! |
fixed in 5.4.2, please let me know whether it fixed your issue |
Hmm, looking at the changes in the commit and the updated tests it should. But it seems that somehow the npm package was not correctly updated. If I look at dist/es5m/index.js directly at https://www.npmjs.com/ it looks like its still the old code:
|
oh... you are right! pnpm follows yarn and disabled post/pre hooks - https://pnpm.io/cli/run#differences-with-npm-run . I didn't know that... |
released 5.4.3 |
It works now! |
So, no other changes are required? may I close this issue? |
Yes :) |
AWESOME! Thank you @gunters63 very much for being so responsive and helpful! |
Describe the bug
Sporadically I get the following error from useAbility:
To Reproduce
When I trigger an ability change via an AbilityContext upstream in the UI tree, every 5 to 10 re-renders the error occurs. useAbility is used by a primitive UI control wrapper component used a lot in my project ( I use CASL ability for controlling write access, which re-renders UI controls to a read-only state when use level is not high enough).
Expected behavior
No race condition
Interactive example (optional, but highly desirable)
I have a large hierarchy and large parts of the UI tree gets sometimes unmounted as a response of an ability change.
So it is really difficult to reproduce this error in a minimal project.
As an alternative I tried already a bug fix which seems to work.
I replace the original implementation of the useAbility:
with this:
Basically I currently suspect the race condition somewhere inside the unsubscribe function returned from ability.on and just don't call setRules (which triggers a setState and the bug) anymore as soon as the component is unmounted.
This fixes the racing condition 100%
CASL Version
"@casl/ability": "^5.4.0",
"@casl/react": "^2.3.0",
Environment:
NodeJS on Windows, newest Chrome
The text was updated successfully, but these errors were encountered: