How to create a setter for an observable prop
See original GitHub issueI noticed a repeated pattern in my code setup, usually in my store i would have some observables defined as follows:
@observable loading: boolean = true;
@observable name: string = '';
// ..
And then for each observable, i would create a matching setter action:
@action setLoading = (value: boolean) => {
this.loading = value;
};
@action setName = (value: string) => {
this.name = value;
};
// ..
Later on in my components that have the store injected in them, i can easily access this.props.store.name
and this.props.store.setName('foo')
.
This is not an issue when you have few observables, but for a large store with tens of observables, this becomes a tedious process and could lead to having errors when we wish to modify that pice of code, say the name of the observable for example. mobx should automate this for us i thought to my self, and indeed i found out about observable.box
, although it requirers using .get()
to retrieve the value of the observable, it would’ve been more convenient to have .get()
automatically returned if .set()
is not called.
A perfect api would be something like this:
loading: boolean = observable(true);
// or with decorators (more convenient)
@observable loading: boolean = true;
// or if it cannot be implemented directly for some reason
@observable @box loading: boolean = true;
// or just @box
@box loading: boolean = true;
// usage
console.log(this.props.store.loading); // prints true
this.props.store.loading.set(false);
console.log(this.props.store.loading); // prints false
Can this be done currently with mobx? did i miss something in the docs? if not, any thoughts on implementing something like this?
Coming back to react native, i’ve tried using boxed values:
test: string = observable('foo');
// also tried
test: string = observable.box('foo');
console.log(this.props.store.test);
this.props.store.test.set('voila!');
console.log(this.props.store.test);
// also tried console.log(this.props.store.test.get());
An error is being thrown in all cases:
Please advise on this, thanks.
Issue Analytics
- State:
- Created 7 years ago
- Comments:70 (18 by maintainers)
@mweststrate I create setters because they are needed to mutate observables, aren’t they? how else would I update the value, or in the first place why would one create an observable if it was not changing? I don’t mutate the data directly outside the store like this
this.observable = 'bar';
, instead i wrap it in a function/setter/action to ensure consistency across the app.I understand that piece of code you provided, it’s now clear to me that i misused the api (maybe we can polish the docs on that a little) and it’s perhaps not rn related. What I am proposing is that by default mobx would define the setter attached to the observable as
set x (v) { this._x = v }
, and unless it was overwritten by a custom logic as you pointed out, this would be the default behavior. This saves us the headache of writing some repeated boilerplate. mobx is is all about automating things for us, and it’s already doing an excellent job, i think this minor update would make a significant impact. Just by having this I would delete +250 lines of code of an application that I am working on as we speak.I am proposing that just by writing one line
@observable hello = 'world!';
you can directly read the value from the storestore.hello
and set itstore.hello.set('new world!');
Instead of:
get() is already automated, set() is not. What is your view on adding this? https://github.com/mobxjs/mobx/issues/69 could be related.
I think this proposal completely defeats the purpose of strict mode and
@action
. Actions should represent exactly that: actions that the user performs and that should be traceable in the dev tools, etc. Having a simple way to just set values directly goes against this. If you have an action to set a single property, it should be marked and easily identifiable, just like any other action that is more complex.To give you guys an example, in our codebase we make sure we don’t even have any actions in our models. The models define the data we store and can contain methods to modify that data, but they contain no actions at all. All the actions go in services/stores that are called by the view. And they should have as little granularity as possible. When something goes wrong, we only have a few places that we need check in the services/stores to find the problem. If you try to modify the state directly MobX will throw.
Basically you get a nice one-way flux where your view renders the state, and then calls actions in the stores/services to update the state instead of modifying the state directly. This is similar to Redux but in a mutable way and without all the boilerplate: instead of an immutable state tree, action types, and action creators and all that, you call action methods that mutate the state and MobX takes care of the rest.