Run dispose before effect when keys change (#335)#489
Run dispose before effect when keys change (#335)#489jezsung wants to merge 3 commits intorrousselGit:masterfrom
Conversation
When useEffect keys change, the old effect's dispose callback now runs before the new effect is initialized. This matches the expected React useEffect behavior where cleanup runs before the new effect.
📝 WalkthroughWalkthroughHook disposal timing was changed: when a hook is replaced by a different type, its state is disposed immediately (with errors reported), then a new state is created. Tests were updated and expanded to reflect and validate the new dispose-before-recreate semantics. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Fix all issues with AI agents
In `@packages/flutter_hooks/lib/src/framework.dart`:
- Around line 479-482: The inline call to _currentHookState!.value.dispose()
lacks error handling and can abort creation of the new hook state; wrap that
dispose() call in a try-catch and, if an exception occurs, report it using
FlutterError.reportError with a FlutterErrorDetails (include exception, stack,
library/context like 'flutter_hooks: dispose in update hook', and a helpful
ErrorDescription) before continuing to set _currentHookState!.value =
_createHookState<R>(hook), matching the pattern used in unmount() to ensure the
new hook state is still created even if dispose() throws.
Wrap dispose() call in try-catch when keys change to ensure the new hook state is created even if dispose throws. This matches the error handling pattern used in unmount().
Summary
See #335
This PR changes the execution order of
useEffectdispose and effect callbacks when keys change. Previously, the new effect ran before the old effect's dispose callback. Now, dispose runs first, matching React'suseEffectbehavior.Problem
When
useEffectkeys changed, the execution order was:This could cause issues when the dispose callback needs to clean up resources before the new effect initializes them.
Solution
Changed the execution order so that when keys change:
Tests
Added regression tests to verify the execution order:
dispose runs before effect when keys changedispose runs before effect on every build when keys are not provideddispose and effect run in group for each hook in declaration orderAdded tests to ensure errors in dispose are handled properly:
errors in dispose are handled when keys changeerrors in dispose are handled on unmountUpdated existing tests to match the corrected behavior:
useEffect disposer called whenever callback called(use_effect_test.dart)hooks are disposed in reverse order when their keys changes(hook_widget_test.dart)keys recreate hookstate(hook_widget_test.dart)Note on React Compatibility
This PR still does not fully match React's
useEffectexecution order due to fundamental architectural differences between Flutter and React:Multiple hooks in the same widget: React batches all disposes before all effects (
dispose1 → dispose2 → effect1 → effect2). This PR maintains synchronous per-hook execution (dispose1 → effect1 → dispose2 → effect2) to preserve backward compatibility with existing code that relies on effects running immediately during build.Parent-child ordering: React runs child effects before parent effects (bottom-up) and parent cleanups before child cleanups on unmount (top-down). Achieving this in Flutter would require deferring effects to a post-frame callback, which would be a breaking change to the synchronous execution model.
Whether flutter_hooks should fully match these React behaviors is debatable. Doing so would introduce significant breaking changes, and it's unclear whether React's execution model is inherently better than Flutter hooks' model.
Summary by CodeRabbit
Bug Fixes
Tests
✏️ Tip: You can customize this high-level summary in your review settings.