Implement "deep" @observable property
gnaeus opened this issue · 8 comments
Expected behaviour:
class ViewModel {
@reactive
deepObservable = { // like @observable
firstName: "Clive Staples", // like @observable
lastName: "Lewis", // like @observable
array: [1, 2, 3], // like @observableArray
object: { // like @observable({ deep: true })
foo: "bar", // like @observable
reference: null, // like @observable({ deep: true })
},
}
}
const vm = new ViewModel();
vm.deepObservable.object.reference = {
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
};
vm.deepObservable.array.push({
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
});
Will the variable itself be observable?
class ViewModel {
@observable({ deep: true }) obj = {
firstName: "Clive Staples", // like @observable
lastName: "Lewis", // like @observable
array: [1, 2, 3], // like @observableArray
object: { // like @observable({ deep: true })
foo: "bar", // like @observable
reference: null, // like @observable({ deep: true })
},
}
}
(async () => {
const vm = new ViewModel();
vm.obj.lastName = 'Foobar'; // Works, cool
vm.obj = await fetch('some/api').then((response) => response.json()); // Will this work?
})();
Yes, I think it should be observable.
I update description.
Great.
There are 2 typos, it needs to be:
const vm = new ViewModel();
vm.obj.object.reference = {
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
};
vm.obj.array.push({
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
});
or
const vm = new ViewModel().obj;
vm.object.reference = {
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
};
vm.array.push({
firstName: "Clive Staples", // make @observable
lastName: "Lewis", // make @observable
});
How can I help?
Thank you! I fix description again.
You can create pull request if you want to implement this functionality yourself.
Or you can help with writing unit tests for this.
I would go with unit tests, since I'm not in the code of yours. Cool!
@krnlde, today I push first implementation of observable({ deep: true })
with few unit tests.
You can see it in src/__tests__/deep-observable-test.ts
.
But the code needs refactoring and many more unit tests to cover edge cases.
So I don't update /dist/
files and NPM package.
And I think that @observable({ deep: true })
needs one-word shorthand alias, for example @reactive
:
class ViewModel {
@observable({ deep: true }) first = {};
// will be equivalent to
@reactive first = {};
}
Also an utility function for creating "deep observable" objects would be useful.
We can name it track()
like in Steven Sanderson's knockout-es5 or again reactive()
class ViewModel {
first = 123; // make @observable;
second = "test"; // make @observable;
constructor() {
track(this);
// or
reactive(this);
}
}
// and
let array = track([1, 2, 3]); // like observableArray
// or
let obj = reactive({
foo: "bar", // make @observable
});
The new "deep" observable array wrapper can even track assignments
to some index (arr[i] = "something"
) definitelly like in MobX.
And also I create simple benchmark for "deep" observableArray
in src/__tests__/deep-observable-benchmark.ts
.
It seems that using our "deep" observableArray
is about 2 times slower than native ko.observableArray
and about 100 times slower than plain JavaScript Array without change tracking.
Man you're fast! I could not yet find time to add tests and review the code. Sorry for that. I think I'll find some time near the end of the coming week. Will ping you back soon.
It's because I use my library in production. And our app is in active development now =)
During the last week I refactor many parts of the library and add about 30 new test.
At the next week I will try to release version 0.9.0 (and update readme) with our "deep oservable" functionality.
I decide not to add a new option to @observable
decorator. Instead, I added new @reactive
decorator.
So now @observable
means "shallow observable" and @reactive
means "deep observable".
You can find tests for that in src/__tests__/reactive-decorator-test.ts
.
But I think, it may not cover all edge cases. So, your tests are welcome.
And also I drop arr[i] = "something"
change tracking functionality. Because it slows down Knockout's foreach
binding, and increases library size.