Skip to content

Commit

Permalink
Avoid unnecessary observable notifications of Iterable or Map fields
Browse files Browse the repository at this point in the history
  • Loading branch information
amondnet committed Nov 2, 2023
1 parent a709c22 commit e0fb33d
Show file tree
Hide file tree
Showing 2 changed files with 81 additions and 84 deletions.
6 changes: 2 additions & 4 deletions mobx/lib/src/core/atom_extensions.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ extension AtomSpyReporter on Atom {

void reportWrite<T>(T newValue, T oldValue, void Function() setNewValue,
{EqualityComparer<T>? equals}) {
final areEqual = equals == null
? equatable(oldValue, newValue)
: equals(oldValue, newValue);
final areEqual = equals ?? equatable;

// Avoid unnecessary observable notifications of @observable fields of Stores
if (areEqual) {
if (areEqual(newValue, oldValue)) {
return;
}

Expand Down
159 changes: 79 additions & 80 deletions mobx/test/atom_extensions_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -4,118 +4,118 @@ import 'package:test/test.dart';
void main() {
test(
'when write to @observable field with changed value, should trigger notifications for downstream',
() {
final store = _ExampleStore();
() {
final store = _ExampleStore();

final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value));
final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value));

expect(autorunResults, ['first']);
expect(autorunResults, ['first']);

store.value = 'second';
store.value = 'second';

expect(autorunResults, ['first', 'second']);
});
expect(autorunResults, ['first', 'second']);
});

// fixed by #855
test(
'when write to @observable field with unchanged value, should not trigger notifications for downstream',
() {
final store = _ExampleStore();
() {
final store = _ExampleStore();

final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value));
final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value));

expect(autorunResults, ['first']);
expect(autorunResults, ['first']);

store.value = store.value;
store.value = store.value;

expect(autorunResults, ['first']);
});
expect(autorunResults, ['first']);
});

test(
'when write to @alwaysNotify field with unchanged value, should trigger notifications for downstream',
() {
final store = _ExampleStore();
() {
final store = _ExampleStore();

final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value2));
final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value2));

expect(autorunResults, ['first']);
expect(autorunResults, ['first']);

store.value2 = store.value2;
store.value2 = store.value2;

expect(autorunResults, ['first', 'first']);
});
expect(autorunResults, ['first', 'first']);
});

test(
'when write to @MakeObservable(equals: "a?.length == b?.length") field with changed value and not equals, should trigger notifications for downstream',
() {
final store = _ExampleStore();
() {
final store = _ExampleStore();

final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value3));
final autorunResults = <String>[];
autorun((_) => autorunResults.add(store.value3));

expect(autorunResults, ['first']); // length: 5
expect(autorunResults, ['first']); // length: 5

// length: 5, should not trigger
store.value3 = 'third';
// length: 5, should not trigger
store.value3 = 'third';

expect(autorunResults, ['first']);
expect(autorunResults, ['first']);

// length: 6, should trigger
store.value3 = 'second';
// length: 6, should trigger
store.value3 = 'second';

expect(autorunResults, ['first', 'second']);
});
expect(autorunResults, ['first', 'second']);
});

test(
'when write to @observable iterable field with unchanged value, should not trigger notifications for downstream',
() {
final store = _ExampleStore();
'when write to iterable @observable field with unchanged value, should not trigger notifications for downstream',
() {
final store = _ExampleStore();

final autorunResults = <List<String>>[];
autorun((_) => autorunResults.add(store.list));
final autorunResults = <List<String>>[];
autorun((_) => autorunResults.add(store.list));

store.list = ['first'];
expect(autorunResults, [
['first']
]);
store.list = ['first'];
expect(autorunResults, [
['first']
]);

store.list = ['first'];
expect(autorunResults, [
['first']
]);
store.list = ['first'];
expect(autorunResults, [
['first']
]);

store.list = ['first'];
expect(autorunResults, [
['first']
]);
});
store.list = ['first'];
expect(autorunResults, [
['first']
]);
});

test(
'when write to @observable map field with unchanged value, should not trigger notifications for downstream',
() {
final store = _ExampleStore();

final autorunResults = <Map<String, int>>[];
autorun((_) => autorunResults.add(store.map));

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);
});
'when write to map @observable field with unchanged value, should not trigger notifications for downstream',
() {
final store = _ExampleStore();

final autorunResults = <Map<String, int>>[];
autorun((_) => autorunResults.add(store.map));

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);

store.map = {'first': 1};
expect(autorunResults, [
{'first': 1}
]);
});
}

class _ExampleStore = __ExampleStore with _$_ExampleStore;
Expand Down Expand Up @@ -159,7 +159,7 @@ mixin _$_ExampleStore on __ExampleStore, Store {

// ignore: non_constant_identifier_names
late final _$value2Atom =
Atom(name: '__ExampleStore.value2', context: context);
Atom(name: '__ExampleStore.value2', context: context);

@override
String get value2 {
Expand All @@ -176,7 +176,7 @@ mixin _$_ExampleStore on __ExampleStore, Store {

// ignore: non_constant_identifier_names
late final _$value3Atom =
Atom(name: '__ExampleStore.value3', context: context);
Atom(name: '__ExampleStore.value3', context: context);

@override
String get value3 {
Expand All @@ -189,8 +189,7 @@ mixin _$_ExampleStore on __ExampleStore, Store {
_$value3Atom.reportWrite(value, super.value3, () {
super.value3 = value;
},
equals: (String? oldValue, String? newValue) =>
oldValue?.length == newValue?.length);
equals: _equals);
}

// ignore: non_constant_identifier_names
Expand Down

0 comments on commit e0fb33d

Please sign in to comment.