You signed in with another tab or window.Reload to refresh your session.You signed out in another tab or window.Reload to refresh your session.You switched accounts on another tab or window.Reload to refresh your session.Dismiss alert
*A tiny library for creating state machines in Redux apps.*
6
-
5
+
*A tiny lib (12 lines) for creating state machines as swappable Redux reducers*
7
6
8
7
redux-machine enables you to create[reducers](http://redux.js.org/docs/basics/Reducers.html) that can transition between different "statuses." These are likes states in a[finite state machine](https://en.wikipedia.org/wiki/Finite-state_machine). The goal is for redux-machine to support complex workflows simply while keeping all state in the redux store. Keeping all state in the store is good because:
9
8
10
9
- redux-machine works with time-travel debugging. Time-travel debugging was the main[motivation for building redux itself](https://www.youtube.com/watch?v=xsSnOQynTHs).
11
10
- Debugging is easy because information is in one place (the store).
12
11
- Statuses such are queryable by the user interface. This is helpful if you want to show things to the user such as loading spinners to indicate status
13
12
14
-
redux-saga and redux-observable also make it easy to create workflows in redux apps. They are more beautiful and powerful than redux-machine, but can break the "all state is in the store" paradigm.
15
-
16
13
##Install
17
14
18
15
`npm install redux-machine --save`
19
16
20
-
>redux-machine internally uses Object.assign and Symbol, whichareES2015features. If you need to support older browsers, you can use a polyfill such as[core-js](https://github.com/zloirock/core-js#basic).
17
+
>redux-machine internally uses Object.assign, whichis anES2015feature. If you need to support older browsers, you can use a polyfill such as[core-js](https://github.com/zloirock/core-js#basic).
21
18
22
-
##API
19
+
##How to Use
23
20
24
-
-**`createMachine(reducerObject)`** returns a machine, which is itself a reducer
25
-
26
-
`reducerObject` is an object where the keys are statuses and the values are sub-reducers. For example, this code creates a reducer that acts like`initReducer` when the status is`INIT` and acts like`inProgressReducer` when the status is`IN_PROGRESS`:
21
+
This is the entire API for redux-machine:
27
22
28
23
```js
24
+
// entire API, no middleware required
25
+
import {createMachine } =from'./index.js'
26
+
29
27
constfetchUsersReducer=createMachine({
30
28
'INIT': initReducer,
31
29
'IN_PROGRESS': inProgressReducer
32
30
})
33
31
```
34
32
35
-
`reducerObject` must have an`INIT` key. The value for the`INIT` key is the reducer for the starting status of the machine/reducer.
36
-
37
-
The reducer returned by`createMachine` adds a`STATUS` key to the store. That is, when the status is`INIT`, the value of`STATUS` is`INIT` and when the status is`IN_PROGRESS`, the value of`STATUS` is`IN_PROGRESS`. This is useful for debugging or for acting on the current status in the UI or in your API callers.
38
-
39
-
-**`become`** (symbol)
40
-
The reducers that are the values of`reducersObject` can use`become` to transition the machine/reducer to a different status. For example, the following code in`initReducer` transitions`fetchUsersReducer` to the`IN_PROGRESS` status when the`` action is dispatched. At that point,`fetchUsersReducer` will act like`inProgressReducer` and the value of`STATUS` changes to`IN_PROGRESS`:
41
-
42
-
```js
43
-
import {become }from'redux-machine'
44
-
45
-
...
46
-
switch (action.type) {
47
-
case'FETCH_USERS':
48
-
returnObject.assign({}, state, {
49
-
error:null,
50
-
[become]:'IN_PROGRESS'
51
-
})
52
-
case default:
53
-
return state
54
-
}
55
-
```
56
-
57
-
>redux-machine's`become` is influenced by[Akka's`become`](http://doc.akka.io/docs/akka/snapshot/scala/actors.html#become-unbecome).
58
-
59
-
##Full Example
33
+
The reducer returned by`createMachine` will act like`initReducer` when its status is`INIT` and will act like`inProgressReducer` when the status is`IN_PROGRESS`. If the store's`state.status` is undefined, the reducer for`INIT` is used (so it's a good idea to provide a reducer for the`INIT` status).
34
+
>>>>>>>update API
60
35
61
-
Suppose you are making an API call to fetch users, and want only one instance of this API call to happen at a time. You also want to communicate to the user the status of the API call:`INIT` (initial status, no call in progress) and`IN_PROGRESS`.
62
-
63
-
You can use redux-machine to have the following status transitions in your reducer:
64
-

65
-
- When the status is`INIT` and the action type is`FETCH_USERS`, the machine transitions to`IN_PROGRESS` status.
66
-
- When the status is`IN_PROGRESS` and the action type is`FETCH_USERS_RESPONSE` or`FETCH_USERS_FAIL`, the machine transitions to the`INIT` (initial) status.
67
-
68
-
I'll walk through how to create the machine in this example. If you prefer to see all the code in one place, it's in`./test.js`.
69
-
70
-
###`import` redux-machine
71
-
72
-
```js
73
-
import {createMachine,become }from'./index.js'
74
-
```
75
-
76
-
###Create Reducers
77
-
78
-
Next, define a reducer for each status in the machine ('INIT' and 'IN_PROGRESS'):
36
+
`initReducer` and`inProgressReducer` can do status transitions by setting`state.status`:
>The only special part of these reducers is how they use the`become` symbol, which I'll explain in the next section.
115
-
116
-
###Create the Machine
73
+
The example above defines the following state machine:
117
74
118
-
Here's how you can use the`createMachine` function to create a reducer from the`initReducer` and`inProgressReducer`:
119
-
120
-
```js
121
-
constfetchUsersReducer=createMachine({
122
-
'INIT': initReducer,
123
-
'IN_PROGRESS': inProgressReducer
124
-
})
125
-
```
75
+

126
76
127
-
>`fetchUsersReducer` is a normal[reducer](http://redux.js.org/docs/basics/Reducers.html) that you can use directly in your Redux app, with no need for special middleware.
77
+
In words:
78
+
- When the status is`INIT` and the action type is`FETCH_USERS`, the machine transitions to`IN_PROGRESS` status.
79
+
- When the status is`IN_PROGRESS` and the action type is`FETCH_USERS_RESPONSE` or`FETCH_USERS_FAIL`, the machine transitions to the`INIT` (initial) status.
128
80
129
-
`initReducer` is the value for`INIT`, so`createMachine` knows to start off acting like`initReducer`.
81
+
##Making Finite State Machine Reducers without a Library
130
82
131
-
In the object provided to`createMachine`, the names of the keys matter because they are used within reducers for transitioningthemachine/reducer between statuses. For example, the following codein`initReducer` says that whentheaction is of type`FETCH_USERS`, transition the machine to the`IN_PROGRESS` status:
83
+
You don't need redux-machine, since you can accomplish almostthesame thing asin theexample above by defining`fetchUsersReducer` as follows:
132
84
133
85
```js
134
-
switch (action.type) {
135
-
case'FETCH_USERS':
136
-
returnObject.assign({}, state, {
137
-
error:null,
138
-
[become]:'IN_PROGRESS'
139
-
})
140
-
casedefault:
141
-
return state
86
+
constfetchUsersReducer= (state,action)=> {
87
+
switch (state.status) {
88
+
case'INIT':
89
+
returninitReducer(state, action)
90
+
case'IN_PROGRESS':
91
+
returninProgressReducer(state, action)
92
+
default:
93
+
returninitReducer(state, action)
142
94
}
95
+
}
143
96
```
144
97
145
-
>`become` is a symbol imported fromredux-machine, not a string. Seethe`import` statement in the example above.
98
+
The (marginal) advantages of usingredux-machine over just usingtheFSM pattern is that you can more clearly express intent and write slightly less code.
146
99
147
100
##Asynchronous Effects
148
101
149
-
redux-machinedoes only one thing: help you model explicit status transitions in reducers. Itdoesn't prescribe a way of handling asynchronous effects such as API calls. This leaves it open for you to use[no async effects library](http://stackoverflow.com/a/34599594/2482570),[redux-loop](https://github.com/redux-loop/redux-loop),[redux-thunk](https://github.com/gaearon/redux-thunk),[redux-saga](https://github.com/yelouafi/redux-saga), or anything else.
102
+
redux-machine doesn't prescribe a way of handling asynchronous effects such as API calls. This leaves it open for you to use[no async effects library](http://stackoverflow.com/a/34599594/2482570),[redux-loop](https://github.com/redux-loop/redux-loop),[redux-thunk](https://github.com/gaearon/redux-thunk),[redux-saga](https://github.com/yelouafi/redux-saga), or anything else.
150
103
151
-
That said, redux-machine fits very naturally withredux-loop, since both enhancehow you can usereducers. Here's how you could use redux-machine with redux-loop:
104
+
That said, redux-machine fits very naturally withother tools which enhancethe expressiveness ofreducers, such as redux-loop and redux-side-effect. Here's how you could use redux-machine with redux-loop: