Skip to content

Commit

Permalink
add support for Stage3/2022.3 decorators
Browse files Browse the repository at this point in the history
  • Loading branch information
Matchlighter committed Mar 6, 2023
1 parent 9aee50f commit 6de073c
Show file tree
Hide file tree
Showing 25 changed files with 1,572 additions and 67 deletions.
5 changes: 5 additions & 0 deletions .changeset/afraid-cooks-nail.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"mobx": minor
---

Add 2022.3 Decorators support
1 change: 1 addition & 0 deletions .github/workflows/build_and_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ on:
branches: [main]
pull_request:
branches: [main]
workflow_dispatch: {}

jobs:
build:
Expand Down
72 changes: 63 additions & 9 deletions docs/enabling-decorators.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,69 @@ hide_title: true

<script async type="text/javascript" src="//cdn.carbonads.com/carbon.js?serve=CEBD4KQ7&placement=mobxjsorg" id="_carbonads_js"></script>

# Enabling decorators {🚀}
# Enabling Decorators

MobX before version 6 encouraged the use of ES.next decorators to mark things as `observable`, `computed` and `action`. However, decorators are currently not an ES standard, and the process of standardization is taking a long time. It also looks like the standard will be different from the way decorators were implemented previously. In the interest of compatibility we have chosen to move away from them in MobX 6, and recommend the use of [`makeObservable` / `makeAutoObservable`](observable-state.md) instead.
After years of alterations, ES decorators have finally reached Stage 3 in the TC39 process, meaning that they are quite stable and won't undergo breaking changes again like the previous decorator proposals have. MobX has implemented support for this new "2022.3/Stage 3" decorator syntax. If you're looking for information on "legacy" decorators, look below or refer to older versions of the documentation.

But many existing codebases use decorators, and a lot of the documentation and tutorial material online uses them as well. The rule is that anything you can use as an annotation to `makeObservable`, such as `observable`, `action` and `computed`, you can also use as a decorator. So let's examine what that looks like:
2022.3 Decorators are supported in Babel (see https://babeljs.io/docs/babel-plugin-proposal-decorators) and in TypeScript (5.0+).

## Usage

```javascript
import { makeObservable, observable, computed, action } from "mobx"

class Todo {
id = Math.random()
@observable accessor title = ""
@observable accessor finished = false

@action
toggle() {
this.finished = !finished
}
}

class TodoList {
@observable accessor todos = []

@computed
get unfinishedTodoCount() {
return this.todos.filter(todo => !todo.finished).length
}
}
```

Notice the usage of the new `accessor` keyword when using `@observable`.
It is part of the 2022.3 spec and is required if you want to use decorators without `makeObservable()`.
That being said, you _can_ do without it if you continue to call `makeObservable()` like so:

```javascript
class Todo {
@observable title = ""

constructor() {
makeObservable(this)
}
}
```

## Changes/Gotchas

MobX' 2022.3 Decorators are very similar to the pre-MobX 6 decorators, so usage is mostly the same, but there are some gotchas:

- `@observable accessor` decorators are _not_ enumerable. `accessor`s do not have a direct equivalent in the past - they're a new concept in the language. We've chosen to make them non-enumerable, non-own properties in order to better follow the spirit of the ES language and what `accessor` means.
The main cases for enumerability seem to have been around serialization and rest destructuring.
- Regarding serialization, implicitly serializing all properties probably isn't ideal in an OOP-world anyway, so this doesn't seem like a substantial issue (consider implementing `toJSON` or using `serializr` as possible alternatives)
- Addressing rest-destructuring, such is an anti-pattern in MobX - doing so would (likely unwantedly) touch all observables and make the observer overly-reactive).
- `@action some_field = () => {}` was and is valid usage (_if_ `makeObservable()` is also used). However, `@action accessor some_field = () => {}` is never valid.

## Legacy Decorators

We do not recommend new codebases that use MobX use legacy decorators until the point when they become an official part of the language, but you can still use them. It does require setup for transpilation so you have to use Babel or TypeScript.

## MobX Core decorators {🚀}

MobX before version 6 encouraged the use of ES.next decorators to mark things as `observable`, `computed` and `action`. While MobX 6 recomneds against using these decorators (an instead using [`makeObservable` / `makeAutoObservable`](observable-state.md)), it is still possible.

```javascript
import { makeObservable, observable, computed, action } from "mobx"
Expand Down Expand Up @@ -52,7 +110,7 @@ When migrating from MobX 4/5 to 6, we recommend to always run the code-mod, to m

Check out the [Migrating from MobX 4/5 {🚀}](migrating-from-4-or-5.md) section.

## Using `observer` as a decorator
### Using `observer` as a decorator

The `observer` function from `mobx-react` is both a function and a decorator that can be used on class components:

Expand All @@ -63,10 +121,6 @@ class Timer extends React.Component {
}
```

## How to enable decorator support

We do not recommend new codebases that use MobX use decorators until the point when they become an official part of the language, but you can still use them. It does require setup for transpilation so you have to use Babel or TypeScript.

### TypeScript

Enable the compiler option `"experimentalDecorators": true` and `"useDefineForClassFields": true` in your `tsconfig.json`.
Expand All @@ -89,7 +143,7 @@ Install support for decorators: `npm i --save-dev @babel/plugin-proposal-class-p

Decorators are only supported out of the box when using TypeScript in `create-react-app@^2.1.1` and newer. In older versions or when using vanilla JavaScript use eject, or the [customize-cra](https://github.com/arackaf/customize-cra) package.

## Disclaimer: Limitations of the decorator syntax
### Disclaimer: Limitations of the legacy decorator syntax

The current transpiler implementations of the 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.
Expand Down
8 changes: 5 additions & 3 deletions jest.base.config.js
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
const fs = require("fs")
const path = require("path")

const tsConfig = "tsconfig.test.json"

module.exports = function buildConfig(packageDirectory, pkgConfig) {
module.exports = function buildConfig(
packageDirectory,
pkgConfig,
tsConfig = "tsconfig.test.json"
) {
const packageName = require(`${packageDirectory}/package.json`).name
const packageTsconfig = path.resolve(packageDirectory, tsConfig)
return {
Expand Down
2 changes: 1 addition & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
const buildConfig = require("./jest.base.config")

module.exports = buildConfig(__dirname, {
projects: ["<rootDir>/packages/*/jest.config.js"]
projects: ["<rootDir>/packages/*/jest.config.js", "<rootDir>/packages/*/jest.config-*.js"]
// collectCoverageFrom: ["<rootDir>/packages/*/src/**/*.{ts,tsx}"]
})
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"packages/*"
],
"resolutions": {
"typescript": "^4.0.2",
"typescript": "^5.0.0-beta",
"recast": "^0.23.1"
},
"repository": {
Expand Down Expand Up @@ -57,7 +57,7 @@
"lodash": "^4.17.4",
"minimist": "^1.2.5",
"mkdirp": "1.0.4",
"prettier": "^2.0.5",
"prettier": "^2.8.4",
"pretty-quick": "3.1.0",
"prop-types": "15.6.2",
"react": "^18.0.0",
Expand All @@ -67,7 +67,7 @@
"tape": "^5.0.1",
"ts-jest": "26.4.1",
"tsdx": "^0.14.1",
"typescript": "^4.0.2"
"typescript": "^5.0.0-beta"
},
"husky": {
"hooks": {
Expand Down
Loading

0 comments on commit 6de073c

Please sign in to comment.