Skip to content

Commit

Permalink
✨ new API - new helpers - tests (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
fabienjuif authored Oct 27, 2017
1 parent dc10376 commit 0b2d70e
Show file tree
Hide file tree
Showing 23 changed files with 1,792 additions and 82 deletions.
136 changes: 127 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# trampss-mst-onaction

Listen to [mobx-state-tree](https://github.com/mobxjs/mobx-state-tree) actions and react to them !
> Make your mobx-state-tree store a real tree and not graph
> Make your mobx-state-tree store a real tree, not a graph
[![CircleCI](https://circleci.com/gh/Trampss/trampss-mst-onaction.svg?style=shield)](https://circleci.com/gh/Trampss/trampss-mst-onaction) [![Coverage Status](https://coveralls.io/repos/github/Trampss/trampss-mst-onaction/badge.svg?branch=master)](https://coveralls.io/github/Trampss/trampss-mst-onaction?branch=master) [![NPM Version](https://badge.fury.io/js/trampss-mst-onaction.svg)](https://www.npmjs.com/package/trampss-mst-onaction)
[![Size](http://img.badgesize.io/Trampss/trampss-mst-onaction/master/index.js.svg)]()
Expand All @@ -11,14 +11,15 @@ Listen to [mobx-state-tree](https://github.com/mobxjs/mobx-state-tree) actions a
- [Why ?](#why)
- [Installation](#installation)
- [API](#api)
- [Examples](#examples)

## Purpose
TODO
The main purpose is to get rid of store interdependencies and to be more into a reactive way of coding.

## Why
TODO, [you can see this issue](https://github.com/mobxjs/mobx-state-tree/issues/486).
[you can see this issue](https://github.com/mobxjs/mobx-state-tree/issues/486).

From graph to a tree:
What we want is to pass from an actions dependencies graph to a tree:
<center>
<img width=400 src="https://user-images.githubusercontent.com/17828231/31930688-e625701e-b8a0-11e7-93d6-2afade1d6f4a.png" />
&nbsp;&nbsp;
Expand All @@ -30,26 +31,143 @@ From graph to a tree:
- `npm i trampss-mst-onaction`

## API
TODO
### First try
1. Import the middleware from **trampss-mst-onaction**: `import onAction from 'trampss-mst-onaction'`
2. Write your reaction, the easiest way is to write it as a function:
```es6
const dispatch = (action, tree) => {
const { fullpath, ended } = action

Example: we want a login action to trigger a router action.
We can do it this way :
if (fullpath === '/auth/login' && ended) {
tree.ui.router.goToList()
}
}
```
3. Connect the middleware to your root store with `addMiddleware` from [mobx-state-tree](https://github.com/mobxjs/mobx-state-tree): `addMiddleware(yourStore, onAction(dispatch))`
4. Voila !

### Middleware API
As you see on the [First try](#first-try) what you have to do is to give a `dispatch` function to the `onAction` middleware.

The `dispatch` function can be of two different types:
- **an array**, in this case, each function of the array will be called
- **a function**, in this case the function will be called
* if the `dispatch` function returns an array, then the middleware will iterate over the array and call each functions that compose it

You can use the `take` helper to avoid dealing with the API and have a cleaner code.

From [First try](#first-try) example code with `take` helper:
```es6
import { addMiddleware } from 'mobx-state-tree'
import onAction from 'trampss-mst-onaction'
import Store from './your-store-model'

// instanciate the store
const store = Store.create({})

// the actions to trigger
const dispatch = (action, tree) => [
take.ended('/auth/login', () => { tree.ui.router.goToList() })
]

// attach the onAction middleware from trampss-mst-onaction
addMiddleware(store, onAction(dispatch))
```
Note that:
- dispatch returns an array
- we call `take.ended` which will test that the asynchronous action is ended
- we pass the full action name (path + name) as first parameter
- we pass the reaction as second one parameter

### Take API
`take` is an helper that takes two arguments (`take(test, reaction)`):
- first argument is the `test`, it can be
* **a string:** then the fullpath equality is tested
* **a regular expression:** then the fullpath is tested over the regular expression
* **a function:** the function is called and should return true to have the reaction called
- the function takes two arguments: the `action` to test and the current `tree` (your store instance)
- second argument is the `reaction`, this is **a function** with two parameters (`reaction(action, tree)`):
* `action` is the action that pass the test (first argument of `take`)
* `tree` is your current store instance, so you can call action on it !

### Action API
As you can see, the `action` object is given to your `dispatch` function, and to first and second parameters of `take` helper.
This `action` owns these fields:
- **path:** the action path from the root store
- **name:** the action name
- **fullpath:** `path + '/' + name`
- **ended:** for asynchronous action **only**, it means the aynchronous action is ended

## Examples
We will write 4 ways of doing a router redirection after our login is successful:
- `dispatch` is a function (that doesn't return an array)
- `dispatch` is a function that returns an array
* with a not pure `take` helper function use
* with a pure `take` helper function use
- `dispatch` is an array

### `dispatch` is a function (that doesn't return an array)
```es6
import { addMiddleware } from 'mobx-state-tree'
import onAction from 'trampss-mst-onaction'
import Store from './your-store-model'

const store = Store.create({})

const dispatch = (action, tree) => {
const { fullpath, ended } = action

if (fullpath === '/auth/login' && ended) {
if (ended && fullpath === '/auth/login') {
tree.ui.router.goToList()
}
}

// attach the onAction middleware from trampss-mst-onaction
addMiddleware(store, onAction(dispatch))
```

### `dispatch` is a function that returns an array - impure take
```es6
import { addMiddleware } from 'mobx-state-tree'
import onAction, { take } from 'trampss-mst-onaction'
import Store from './your-store-model'

const store = Store.create({})

const dispatch = (action, tree) => [
take.ended('/auth/login', () => { tree.ui.router.goToList() }),
]

addMiddleware(store, onAction(dispatch))
```

### `dispatch` is a function that returns an array - **pure** take
```es6
import { addMiddleware } from 'mobx-state-tree'
import onAction, { take } from 'trampss-mst-onaction'
import Store from './your-store-model'

const store = Store.create({})

const dispatch = () => [
take.ended('/auth/login', (action, tree) => { tree.ui.router.goToList() }),
]

addMiddleware(store, onAction(dispatch))
```

### `dispatch` is an array ️❤️
❤️ This is the recommended way of using this middleware ❤️

```es6
import { addMiddleware } from 'mobx-state-tree'
import onAction, { take } from 'trampss-mst-onaction'
import Store from './your-store-model'

const store = Store.create({})

const dispatch = [
take.ended('/auth/login', (action, tree) => { tree.ui.router.goToList() }),
]

addMiddleware(store, onAction(dispatch))
```
2 changes: 1 addition & 1 deletion index.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion index.js.map

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

11 changes: 9 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,30 +1,36 @@
{
"name": "trampss-mst-onaction",
"version": "0.8.1",
"version": "0.10.0",
"main": "index.js",
"repository": "git@github.com:Trampss/trampss-mst-onaction.git",
"author": "Fabien JUIF <fabien.juif@gmail.com>",
"license": "MIT",
"scripts": {
"test": "jest",
"coveralls": "jest --coverage && cat ./coverage/lcov.info | coveralls",
"build:core": "cross-env NODE_ENV=build rollup -c ./misc/rollup.config.js",
"build": "npm-run-all --parallel build:*",
"lint:js": "eslint --ext js src",
"lint": "npm-run-all --parallel lint:*",
"ci": "npm-run-all --parallel lint"
"ci": "npm-run-all --parallel lint coveralls"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-eslint": "^8.0.1",
"babel-plugin-external-helpers": "^6.22.0",
"babel-plugin-transform-object-rest-spread": "^6.26.0",
"babel-preset-es2015": "^6.24.1",
"coveralls": "^3.0.0",
"cross-env": "^5.1.0",
"eslint": "^4.9.0",
"eslint-config-airbnb": "^16.1.0",
"eslint-plugin-babel": "^4.1.2",
"eslint-plugin-import": "^2.8.0",
"eslint-plugin-jsx-a11y": "^6.0.2",
"eslint-plugin-react": "^7.4.0",
"jest": "^21.2.1",
"lodash": "^4.17.4",
"mobx": "^3.3.1",
"mobx-state-tree": "^1.0.1",
"npm-run-all": "^4.1.1",
"rollup": "^0.50.0",
Expand All @@ -33,6 +39,7 @@
"rollup-plugin-uglify": "^2.0.1"
},
"peerDependencies": {
"lodash": "^4.17.4",
"mobx-state-tree": "^1.0.1"
},
"babel": {
Expand Down
32 changes: 32 additions & 0 deletions src/__snapshots__/getAction.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`getAction maper should map a simple mobx-state-tree action 1`] = `
Object {
"args": Array [
"first",
Object {
"second": "argument",
},
],
"fullpath": "/this/is/a/path/actionName",
"name": "actionName",
"path": "/this/is/a/path",
}
`;

exports[`getAction maper should map an asynchronous action that ends 1`] = `
Object {
"args": Array [
"first",
Object {
"second": "argument",
},
],
"ended": true,
"fullpath": "/this/is/a/path/actionName",
"name": "actionName",
"path": "/this/is/a/path",
}
`;

exports[`getAction maper should map an undefined type 1`] = `undefined`;
57 changes: 57 additions & 0 deletions src/__snapshots__/middleware.spec.js.snap
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`middleware should call runArray 1`] = `"i am next"`;

exports[`middleware should call runArray 2`] = `
Array [
Array [
Array [],
Object {
"call": Object {
"tree": Object {
"this": "is tree",
},
"type": "action",
},
"mocked": "getAction",
},
Object {
"this": "is tree",
},
],
]
`;

exports[`middleware should call runArray 3`] = `Array []`;

exports[`middleware should call runFunction 1`] = `"i am next"`;

exports[`middleware should call runFunction 2`] = `Array []`;

exports[`middleware should call runFunction 3`] = `
Array [
Array [
[Function],
Object {
"call": Object {
"tree": Object {
"this": "is tree",
},
"type": "action",
},
"mocked": "getAction",
},
Object {
"this": "is tree",
},
],
]
`;

exports[`middleware should not call runner (no action) 1`] = `"i am next"`;

exports[`middleware should not call runner (no action) 2`] = `Array []`;

exports[`middleware should not call runner (no action) 3`] = `Array []`;

exports[`middleware should throw an error (unknown dispatch type) 1`] = `[Error: [trampss-mst-onaction] unknow dispatch type]`;
26 changes: 26 additions & 0 deletions src/getAction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { getPath } from 'mobx-state-tree'

export default (call) => {
const {
name,
type,
context,
args,
} = call

const path = getPath(context)
const fullpath = `${path}/${name}`

const action = {
fullpath,
path,
name,
args,
}

switch (type) {
case 'process_return': return { ...action, ended: true }
case 'action': return action
default: return undefined
}
}
Loading

0 comments on commit 0b2d70e

Please sign in to comment.