This is a forked repo to experiment with adding a few more decorators to the totally amazing work farwayer/mobx-decorators has already accomplished.
I'm experimenting with adding
@reaction(()=>data,options?)
which would be decorator version of reaction() in mobx. the instance method would be executed and passed the data. i like the single file for classes with observables, i like that i can have a section with all my actions, i would like to do the same with reactions. it would be nice to have reactions as methods. Maybe because they are not so decoupled. But most importantly, they can be hard to see in the code, especially if you are modifying someone else's code.i would say that the default behaviour would be that the reactions are started and stopped when the object is constructed and then removed. i can also see the use of an@init
and a@dispose
@session
which is a trivial alteration of @save using sessionStorage.@init
to mark a method as one that when executed initializes reactions immediately after@dispose
to mark a method where reactions and autoruns should be disposed@save
experimental modifications i would like a property marked by save to be able to have a default value that could be set directly in the class declaration. at first i thought it would be interesting to also make the saving and loading triggered by an @init, but upon reflection it's pretty much irelevant, since any initializing action would usually involve setting a state that would override the storage -- a more up to date version of a state value. but a default value would be useful
Several helper MobX decorators
npm install --save mobx-decorators
You also should use some transpiler (like babel).
npm install --save-dev @babel/plugin-proposal-decorators
@setter
@setter(name)
@setter(name, constValue)
@setter(transformFn: value =>)
@setter(name, transformFn: value =>)
Create setter for property
with setProperty
or custom name.
If constValue
provided this value will be set every time setter called.
You can also provide transform function.
With transformFn
function you can change value that will be set.
import {observable} from 'mobx'
import {setter} from 'mobx-decorators'
class User {
@setter @observable loggedIn = false;
}
const user = new User();
user.setLoggedIn(true); // user.loggedIn = true
class User {
@setter('updateLoggedIn')
@observable loggedIn = false;
}
const user = new User();
user.updateLoggedIn(true); // user.loggedIn = true
class User {
@setter('login', true)
@setter('logout', false)
@observable loggedIn = false;
}
const user = new User();
user.login(); // user.loggedIn = true
user.logout(); // user.loggedIn = false
class User {
@setter(value => value && value.toUpperCase())
@observable name;
}
const user = new User();
user.setName('Alice'); // user.name = 'ALICE'
@toggle
@toggle(name)
Toggle boolean property (property = !property
).
import {observable} from 'mobx'
import {toggle} from 'mobx-decorators'
class User {
@toggle @observable loggedIn = false;
}
const user = new User();
user.toggleLoggedIn(); // user.loggedIn = !user.loggedIn
class User {
@toggle('swapLoggedIn')
@observable loggedIn = false;
}
const user = new User();
user.swapLoggedIn(); // user.loggedIn = !user.loggedIn
@observe(onChanged: change =>)
@observe(onChanged: change =>, invokeBeforeFirstAccess)
onChanged
will be called after property change.
If invokeBeforeFirstAccess
is true
handler will be called one time before
property first access (set or get).
More info can be found in mobx docs
import {observable} from 'mobx'
import {observe} from 'mobx-decorators'
class User {
@observe(change => console.log(change.newValue))
@setter @observable loggedIn = false;
}
const user = new User();
user.setLoggedIn(true); // console.log(true)
class User {
@observe(change => console.log(change.newValue), true)
@setter @observable loggedIn = false;
}
const user1 = new User();
const loggedIn = user.loggedIn; // console.log(false)
user1.setLoggedIn(true); // console.log(true)
const user2 = new User();
user2.setLoggedIn(true); // console.log(false)
// console.log(true)
@intercept(onWillChange: change =>)
onWillChange
will be called before property change.
You can replace value or cancel change in handler.
More info can be found in mobx docs
import {observable} from 'mobx'
import {intercept} from 'mobx-decorators'
class User {
@intercept(change => {
change.newValue = 999;
return change;
})
@setter @observable loginCount = 0;
}
const user = new User();
user.setLoginCount(1); // user.loginCount = 999;
class User {
@intercept(change => null)
@setter @observable loginCount = 0;
}
const user = new User();
user.setLoginCount(1); // user.loginCount = 0;
@_interceptReads(onRead: value =>)
interceptReads renamed in Mobx4 and look like will be deprecated
onRead
will be called before property reading.
You can transform value in handler.
More info can be found in mobx CHANGELOG
import {observable} from 'mobx'
import {_interceptReads} from 'mobx-decorators'
class User {
@_interceptReads(value => value && value.toUpperCase())
@observable name = 'Alice';
}
const user = new User();
console.log(user.name) // ALICE
@save
@save({
storage = defaultStorage(),
storeName = store => store.storeName,
serializer = {
save: value => JSON.stringify(value),
load: data => JSON.parse(data),
},
onLoaded = (store, property, value) => {},
onSaved = (store, property, value) => {},
onInitialized = (store, property, value) => {},
})
createSaveDecorator(baseOptions={})
(!) TypeScript: you can't use class property initializers (class F {prop = 1}
) with @save decorator
@save
decorator helps save and load observable value to/from permanent
storage. Keep in mind @save
is lazy decorator and loading will be started
only after first property access. If you change property before or during
loading than restored value will be ignored.
onLoaded
callback will be called only if value is loaded from storage.
onSave
will be called after saving.
onInitialized
will be called after loading attempt independent of the result.
By default values saved as json
. In some cases (Date
for example) you should
provide serializer
(see example with date).
You must define storeName
property in store (see examples) or pass it as
option. storeName
option can be string or function.
Default storage is localStorage
for browser, AsyncStorage
for React Native and memory for other platforms. You can specify you own
(localForage for example)
by storage
option. Storage must realize simple interface
(functions are async or return Promise):
const MyStorage = {
async getItem(key) {
// return item
},
async setItem(key, value) {
// save item
}
}
If you need to pass the same options (storage for example) to @save
decorator
of several properties than you can use createSaveDecorator
function.
import {observable} from 'mobx'
import {save, createSaveDecorator} from 'mobx-decorators'
class User {
storeName = 'user';
@save @observable loginCount;
}
const user = new User();
console.log(user.loginCount); // undefined
// @save will try to load loginCount from storage but
// loading is async (!) so value is still undefined here
class User {
storeName = 'user';
// storage contains 999 for loginCount property
@save({
onInitialized: (store, property, value) => {
console.log(property, value); // 'loginCount', 999
console.log(store.loginCount); // 999
},
onLoaded: (store, property, value) => {
console.log(property, value); // 'loginCount', 999
console.log(store.loginCount); // 999
},
onSave: (store, property, value) => {
console.log(property, value); // 1000
}
})
@setter @observable loginCount;
}
const user = new User();
console.log(user.loginCount); // undefined, loading loginCount
// throw some time
console.log(user.loginCount); // 999
user.setLoginCount(1000); // 1000 will be saved to storage
class User {
@save({
storeName: 'user',
})
@observable loginCount;
@save({
storeName: 'group',
})
@observable group;
}
const user = new User();
console.log(user.loginCount); // undefined, loading loginCount
console.log(user.group); // undefined, loading group
class User {
storeName = 'user';
@save({
serializer: {
load: data => new Date(fromBSON(data)),
save: value => toBSON(value),
},
})
@observable lastLogin;
}
const user = new User();
console.log(user.lastLogin);
const mysave = createSaveDecorator({
storage: MyOwnStorage,
storeName: store => store.constructor.name,
});
class User {
@mysave @observable loginCount;
@save @observable name;
}
const user = new User();
console.log(user.loginCount);
const mysave = createSaveDecorator({
storage: MyOwnStorage,
storeName: 'user',
});
class User {
@mysave({
onInitialized: () => console.log('initialized')
})
@observable loginCount;
@save @observable name;
}
const user = new User();
console.log(user.loginCount);
@allObservable
@allObservable({
only,
except = [],
})
Class decorator that makes all properties observable. Use only
for
whitelisting properties and except
for blacklisting.
import {allObservable} from 'mobx-decorators'
@allObservable
class User {
name = 'unknown';
loginCount = 0;
}
@allObservable({only: ['loginCount']})
class User {
name = 'unknown';
loginCount = 0;
}
@allObservable({except: ['name']})
class User {
name = 'unknown';
loginCount = 0;
}
- new babel transform fixes
- interpret null value from storage as non-existent (localStorage returns null
for non-existent keys)
Bump major version because it can be back incompatible. - .native postfix for React Native
- separate esm build
- MobX4
- major version is the same as MobX
- TypeScript support. Yippee!
@interceptReads
renamed to@_interceptReads
(as in MobX4)@setter
and@toggle
auto bound to store@save
:transform
replaced withserializer
@save
:storeName
option can be function- all internal mobx kitchen depends removed
- prevent webpack to use non-transpiled source by default
- fix missed files in
lib
@interceptReads
- add missed
allObservable
export to react-native
- all callbacks called in store context now
- fix
@observe
,@intercept
and@save
may not work with extending
@allObservable
: fix getter and setter for observable was not defined
@allObservable
@save
,@observe
and@intercept
can be defined after@observable
now- decorators return configurable properties now
- transpiling with es2015 preset now
@setter
: you can provide transform function now
class User {
@setter(value => value && value.toUpperCase())
@observable
name;
}
const user = new User();
user.setName('Alice'); // user.name = 'ALICE'
@save
,@createSaveDecorator
- transpiled version
- Adopting to mobx3
@observe
callback receive change argument now (
more info
)