Skip to content

Commit

Permalink
closes #81
Browse files Browse the repository at this point in the history
  • Loading branch information
rrousselGit committed Jun 20, 2020
1 parent b929f54 commit eb456b2
Show file tree
Hide file tree
Showing 4 changed files with 89 additions and 26 deletions.
29 changes: 29 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
## 0.10.0

**Breaking change**:

- The order in which hooks are disposed has been reversed.

Consider:

```dart
useSomething();
useSomethingElse();
```

Before, the `useSomething` was disposed before `useSomethingElse`.
Now, `useSomethingElse` is disposed before the `useSomething`.

The reason for this change is for cases like:

```dart
// Creates an AnimationController
final animationController = useAnimationController();
// Immediatly listen to the AnimationController
useListenable(animationController);
```

Before, when the widget was disposed, this caused an exception as
`useListenable` unsubscribed to the `AnimationController` _after_ its `dispose`
method was called.

**Non-breaking changes**:

- Added a way for hooks to potentially abort a widget rebuild.
- Added `StatefulHookWidget`, a `StatefulWidget` that can use hooks inside its `build` method.

Expand Down
53 changes: 29 additions & 24 deletions lib/src/animation.dart
Original file line number Diff line number Diff line change
Expand Up @@ -33,16 +33,20 @@ AnimationController useAnimationController({
AnimationBehavior animationBehavior = AnimationBehavior.normal,
List<Object> keys,
}) {
return Hook.use(_AnimationControllerHook(
duration: duration,
debugLabel: debugLabel,
initialValue: initialValue,
lowerBound: lowerBound,
upperBound: upperBound,
vsync: vsync,
animationBehavior: animationBehavior,
keys: keys,
));
vsync ??= useSingleTickerProvider(keys: keys);

return Hook.use(
_AnimationControllerHook(
duration: duration,
debugLabel: debugLabel,
initialValue: initialValue,
lowerBound: lowerBound,
upperBound: upperBound,
vsync: vsync,
animationBehavior: animationBehavior,
keys: keys,
),
);
}

class _AnimationControllerHook extends Hook<AnimationController> {
Expand Down Expand Up @@ -74,13 +78,24 @@ class _AnimationControllerHookState
extends HookState<AnimationController, _AnimationControllerHook> {
AnimationController _animationController;

@override
void initHook() {
super.initHook();
_animationController = AnimationController(
vsync: hook.vsync,
duration: hook.duration,
debugLabel: hook.debugLabel,
lowerBound: hook.lowerBound,
upperBound: hook.upperBound,
animationBehavior: hook.animationBehavior,
value: hook.initialValue,
);
}

@override
void didUpdateHook(_AnimationControllerHook oldHook) {
super.didUpdateHook(oldHook);
if (hook.vsync != oldHook.vsync) {
assert(hook.vsync != null && oldHook.vsync != null, '''
Switching between controller and uncontrolled vsync is not allowed.
''');
_animationController.resync(hook.vsync);
}

Expand All @@ -91,17 +106,7 @@ Switching between controller and uncontrolled vsync is not allowed.

@override
AnimationController build(BuildContext context) {
final vsync = hook.vsync ?? useSingleTickerProvider(keys: hook.keys);

return _animationController ??= AnimationController(
vsync: vsync,
duration: hook.duration,
debugLabel: hook.debugLabel,
lowerBound: hook.lowerBound,
upperBound: hook.upperBound,
animationBehavior: hook.animationBehavior,
value: hook.initialValue,
);
return _animationController;
}

@override
Expand Down
2 changes: 1 addition & 1 deletion lib/src/framework.dart
Original file line number Diff line number Diff line change
Expand Up @@ -410,7 +410,7 @@ This may happen if the call to `Hook.use` is made under some condition.
void unmount() {
super.unmount();
if (_hooks != null) {
for (final hook in _hooks) {
for (final hook in _hooks.reversed) {
try {
hook.dispose();
} catch (exception, stack) {
Expand Down
31 changes: 30 additions & 1 deletion test/hook_widget_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,31 @@ void main() {
reset(reassemble);
});

testWidgets('hooks are disposed in reverse order on unmount', (tester) async {
final first = MockDispose();
final second = MockDispose();

await tester.pumpWidget(
HookBuilder(builder: (c) {
useEffect(() => first);
useEffect(() => second);
return Container();
}),
);

verifyNoMoreInteractions(first);
verifyNoMoreInteractions(second);

await tester.pumpWidget(Container());

verifyInOrder([
second(),
first(),
]);
verifyNoMoreInteractions(first);
verifyNoMoreInteractions(second);
});

testWidgets('StatefulHookWidget', (tester) async {
final notifier = ValueNotifier(0);

Expand Down Expand Up @@ -649,8 +674,8 @@ void main() {
expect(tester.takeException(), 24);

verifyInOrder([
dispose.call(),
dispose2.call(),
dispose.call(),
]);
});

Expand Down Expand Up @@ -1176,6 +1201,10 @@ void main() {
});
}

class MockDispose extends Mock {
void call();
}

class MyHook extends Hook<MyHookState> {
@override
MyHookState createState() => MyHookState();
Expand Down

0 comments on commit eb456b2

Please sign in to comment.