sidebar_label | title | hide_title |
---|---|---|
How to (not) use decorator syntax |
How to (not) use decorators |
true |
Using ES.next decorators in MobX is optional. This section explains how to use them, or how to avoid them.
Advantages of using decorator syntax:
- Minimizes boilerplate, declarative.
- Easy to use and read. A majority of the MobX users use them.
Disadvantages of using decorator syntax:
- Stage-2 ES.next feature
- Requires a little setup and transpilation, only supported with Babel / Typescript transpilation so far
You can approach using decorators in two ways in MobX
- Enable the currently experimental decorator syntax in your compiler (read on)
- Don't enable decorator syntax, but leverage the MobX built-in utility
decorate
to apply decorators to your classes / objects.
Using decorator syntax:
import { observable, computed, action } from "mobx"
class Timer {
@observable start = Date.now()
@observable current = Date.now()
@computed
get elapsedTime() {
return this.current - this.start + "milliseconds"
}
@action
tick() {
this.current = Date.now()
}
}
Using the decorate
utility:
import { observable, computed, action, decorate } from "mobx"
class Timer {
start = Date.now()
current = Date.now()
get elapsedTime() {
return this.current - this.start + "milliseconds"
}
tick() {
this.current = Date.now()
}
}
decorate(Timer, {
start: observable,
current: observable,
elapsedTime: computed,
tick: action,
})
For applying multiple decorators on a single property, you can pass an array of decorators. The decorators application order is from right to left.
import { decorate, observable } from "mobx"
import { serializable, primitive } from "serializr"
import persist from "mobx-persist"
class Todo {
id = Math.random()
title = ""
finished = false
}
decorate(Todo, {
title: [serializable(primitive), persist("object"), observable],
finished: [serializable(primitive), observable],
})
Note: Not all decorators can be composed together, and this functionality is just best-effort. Some decorators affect the instance directly and can 'hide' the effect of other decorators that only change the prototype.
The observer
function from mobx-react
is both a decorator and a function, that means that all these syntax variants will work:
@observer
class Timer extends React.Component {
/* ... */
}
const Timer = observer(class Timer extends React.Component {
/* ... */
})
const Timer = observer((props) => (
/* rendering */
))
If you want to use decorators follow the following steps.
TypeScript
Enable the compiler option "experimentalDecorators": true
in your tsconfig.json
.
Babel 6: using babel-preset-mobx
A more convenient way to setup Babel for usage with mobx is to use the mobx
preset, that incorporates decorators and several other plugins typically used with mobx:
npm install --save-dev babel-preset-mobx
.babelrc:
{
"presets": ["mobx"]
}
Babel 6: manually enabling decorators
To enable support for decorators without using the mobx preset, follow the following steps.
Install support for decorators: npm i --save-dev babel-plugin-transform-decorators-legacy
. And enable it in your .babelrc
file:
{
"presets": ["es2015", "stage-1"],
"plugins": ["transform-decorators-legacy"]
}
Note that the order of plugins is important: transform-decorators-legacy
should be listed first.
Having issues with the babel setup? Check this issue first.
Babel 7
Install support for decorators: npm i --save-dev @babel/plugin-proposal-class-properties @babel/plugin-proposal-decorators
. And enable it in your .babelrc
file:
{
"plugins": [
["@babel/plugin-proposal-decorators", { "legacy": true }],
["@babel/plugin-proposal-class-properties", { "loose": true }]
]
}
Note that the legacy
mode is important (as is putting the decorators proposal first). Non-legacy mode is WIP.
- Decorators are not supported out of the box in
create-react-app@1.*
. To fix this, you can either use thedecorate
utility, eject, or use the react-app-rewired package.
- Decorators are only supported out of the box when using TypeScript in
create-react-app@^2.1.1
. In older versions or when using vanilla JavaScript use either thedecorate
utility, eject, or the customize-cra package.
The current transpiler implementations of decorator syntax are quite limited and don't behave exactly the same. Also, many compositional patterns are currently not possible with decorators, until the stage-2 proposal has been implemented by all transpilers. For this reason the scope of decorator syntax support in MobX is currently scoped to make sure that the supported features behave consistently accross all environments
The following patterns are not officially supported by the MobX community:
- Redefining decorated class members in inheritance trees
- Decorating static class members
- Combining decorators provided by MobX with other decorators
- Hot module reloading (HMR) / React-hot-loader might not work as expected
Decorated properties might not be visible yet on class instances as own property until the first read / write to that property occurred.
(N.B.: not supported doesn't mean it doesn't work, it means that if it doesn't work, reported issues will not be processed until the official spec has been moved forward)