Skip to content

Commit 23c977f

Browse files
committed
Merge branch 'enhancer'
2 parents 2f6aab0 + 2e55ffa commit 23c977f

18 files changed

+1388
-547
lines changed

Diff for: .babelrc

+8-16
Original file line numberDiff line numberDiff line change
@@ -5,40 +5,32 @@
55
["env", {
66
"modules": "commonjs"
77
}],
8-
"stage-3",
9-
"react"
10-
],
11-
"plugins": []
8+
"stage-3"
9+
]
1210
},
1311
"es": {
1412
"presets": [
1513
["env", {
1614
"modules": false
1715
}],
18-
"stage-3",
19-
"react"
20-
],
21-
"plugins": []
16+
"stage-3"
17+
]
2218
},
2319
"umd": {
2420
"presets": [
2521
["env", {
2622
"modules": false
2723
}],
28-
"stage-3",
29-
"react"
30-
],
31-
"plugins": []
24+
"stage-3"
25+
]
3226
},
3327
"test": {
3428
"presets": [
3529
["env", {
3630
"modules": "commonjs"
3731
}],
38-
"stage-3",
39-
"react"
40-
],
41-
"plugins": []
32+
"stage-3"
33+
]
4234
}
4335
},
4436
"comments": false

Diff for: README.md

+121-3
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,14 @@ programming.
7373
__Summary__
7474

7575
- There are no adapters. `redux-most` is only intended to be used with `Most`.
76+
- `redux-most` offers 2 separate APIs: a `redux-observable`-like API, where Epics
77+
get passed an action stream & a store middleware object containing `dispatch` & `getState`
78+
methods, and a stricter, more declarative API, where Epics get passed an action stream & a state stream.
7679
- `combineEpics` takes in an array of epics instead of multiple arguments.
7780
- Standard `Most` streams are used instead of a custom Observable extension.
7881
- `select` and `selectArray` are available instead of the variadic `ofType`.
7982

83+
8084
__Further Elaboration:__
8185

8286
As the name implies, `redux-most` does not offer adapters for use with other reactive
@@ -87,8 +91,23 @@ JavaScript ecosystem right now, and `Most 2.0` will be even better, as it will f
8791
auto-curried API like `lodash/fp` and `ramda`, but for working with streams instead of arrays.
8892
For a preview of what's to come, check out what's going on [here](https://github.com/mostjs/core).
8993

90-
Whereas `comebineEpics` is variadic in `redux-observable`, it's unary in `redux-most`. It takes in
91-
only one argument, an array of epics, instead of individual epics getting passed in as separate
94+
Initially, `redux-most` offered the same API as `redux-observable`, where Epics received an action
95+
stream & a store middleware object containing `dispatch` & `getState` methods. However, it now offers
96+
both that API and another stricter, more declarative API which eliminates the use of `dispatch` &
97+
`getState`. The reason for this is that I rarely found myself using the imperative `dispatch`
98+
method. It's not really needed, because you can use `switch`, `merge`, `mergeArray`, etc. to send
99+
multiple actions through your outgoing stream. This is nice, because it allows you to stay locked into
100+
the declarative programming style the entire time.
101+
102+
However, using `getState` was still required in epics that needed access to the current state. I
103+
wanted a nice, convenient way to access the current state, just like I had for dispatching actions.
104+
So, I created an alternate API where Epics receive a stream of state changes rather than the
105+
`{ dispatch, getState }` object. This state stream, combined with the new `withState` utility function,
106+
let's you use streams for both dispatching actions & accessing the current state, allowing you to stay
107+
focused & in the zone (the reactive programming mindset).
108+
109+
Moving on, whereas `comebineEpics` is variadic in `redux-observable`, it's unary in `redux-most`. It
110+
takes in only one argument, an array of epics, instead of individual epics getting passed in as separate
92111
arguments.
93112

94113
As for streams, I chose not to extend the `Observable` type with a custom `ActionsObservable`
@@ -105,7 +124,7 @@ practice in functional programming to prefer a known number of arguments over a
105124
of arguments. Therefore, `select` is used when we want to filter by a single action type, and
106125
`selectArray` is used when we want to filter by multiple action types (via an array) simultaneously.
107126

108-
Additionally, to better align with the `Most` API, and because these fucntions take a known number
127+
Additionally, to better align with the `Most` API, and because these functions take a known number
109128
of arguments, `select` & `selectArray` are curried, which allows them to be used in either a
110129
fluent style or a more functional style which enables the use of further currying, partial
111130
application, & functional composition.
@@ -161,11 +180,13 @@ const someOtherEpic = pipe(
161180
## API Reference
162181

163182
- [createEpicMiddleware](https://github.com/joshburgess/redux-most#createepicmiddleware-rootepic)
183+
- [createStateStreamEnhancer](https://github.com/joshburgess/redux-most#createstatestreamenhancer-epicmiddleware)
164184
- [combineEpics](https://github.com/joshburgess/redux-most#combineepics-epics)
165185
- [EpicMiddleware](https://github.com/joshburgess/redux-most#epicmiddleware)
166186
- [replaceEpic](https://github.com/joshburgess/redux-most#replaceEpic)
167187
- [select](https://github.com/joshburgess/redux-most#select-actiontype-stream)
168188
- [selectArray](https://github.com/joshburgess/redux-most#selectArray-actiontypes-stream)
189+
- [withState](https://github.com/joshburgess/redux-most#withstate-statestream-actionstream)
169190

170191
---
171192

@@ -204,6 +225,44 @@ export default function configureStore() {
204225

205226
---
206227

228+
### `createStateStreamEnhancer (epicMiddleware)`
229+
230+
`createStateStreamEnhancer` is used to access `redux-most`'s alternate API, which passes
231+
`Epics` a state stream (Ex: `state$`) instead of the `{ dispatch, getState }` store
232+
`MiddlewareAPI` object. You must provide an instance of the `EpicMiddleware`, and the
233+
resulting function must be applied AFTER using `redux`'s `applyMiddleware` if also using
234+
other middleware.
235+
236+
__Arguments__
237+
238+
1. `rootEpic` _(`Epic`)_: The root Epic.
239+
240+
__Returns__
241+
242+
_(`MiddlewareAPI`)_: An enhanced instance of the `redux-most` middleware, exposing a stream
243+
of state change values.
244+
245+
__Example__
246+
```js
247+
import { createStore, applyMiddleware } from 'redux'
248+
import {
249+
createEpicMiddleware,
250+
createStateStreamEnhancer,
251+
} from 'redux-most'
252+
import rootEpic from '../epics'
253+
254+
const epicMiddleware = createEpicMiddleware(rootEpic)
255+
const middleware = [...] // other middleware here
256+
const storeEnhancers = compose(
257+
createStateStreamEnhancer(epicMiddleware),
258+
applyMiddleware(...middleware)
259+
)
260+
261+
const store = createStore(rootReducer, storeEnhancers)
262+
```
263+
264+
---
265+
207266
### `combineEpics (epicsArray)`
208267

209268
`combineEpics`, as the name suggests, allows you to pass in an array of epics and combine them into a single one.
@@ -298,6 +357,11 @@ __Arguments__
298357
1. `actionType` _(`string`)_: The type of action to filter by.
299358
2. `stream` _(`Stream`)_: The stream of actions you are filtering. Ex: `actions$`.
300359

360+
__Returns__
361+
362+
_(Stream)_: A new, filtered stream holding only the actions corresponding to the action
363+
type passed to `select`.
364+
301365
The `select` operator is curried, allowing you to use a fluent or functional style.
302366

303367
__Examples__
@@ -369,6 +433,11 @@ __Arguments__
369433
1. `actionTypes` _(`string[]`)_: An array of action types to filter by.
370434
2. `stream` _(`Stream`)_: The stream of actions you are filtering. Ex: `actions$`.
371435

436+
__Returns__
437+
438+
_(Stream)_: A new, filtered stream holding only the actions corresponding to the action
439+
types passed to `selectArray`.
440+
372441
The `selectArray` operator is curried, allowing you to use a fluent or functional style.
373442

374443
__Examples__
@@ -447,3 +516,52 @@ const clear = compose(
447516

448517
export default clear
449518
```
519+
---
520+
521+
### `withState (stateStream, actionStream)`
522+
523+
A utility function for use with `redux-most`'s optional state stream API. This
524+
provides a convenient way to `sample` the latest state change value. Note:
525+
accessing the alternate API requires using `createStateStreamEnhancer`.
526+
527+
__Arguments__
528+
529+
1. `stateStream` _(`Stream`)_: The state stream provided by `redux-most`'s alternate API.
530+
2. `actionStream` _(`Stream`)_: The filtered stream of action events used to trigger
531+
sampling of the latest state. (Ex: `actions$`).
532+
533+
__Returns__
534+
535+
_(`[state, action]`)_: An Array of length 2 (or Tuple) containing the latest
536+
state value at index 0 and the latest action of the filtered action stream at index 1.
537+
538+
`withState` is curried, allowing you to pass in the state stream & action stream
539+
together, at the same time, or separately, delaying passing in the action stream.
540+
This provides the user extra flexibility, allowing it to easily be used within
541+
functional composition pipelines.
542+
543+
__Examples__
544+
```js
545+
import { select, withState } from 'redux-most'
546+
import { curriedMap as map } from '../utils'
547+
import compose from 'ramda/src/compose'
548+
549+
const accessStateFromArray = ([state, action]) => ({
550+
type: 'ACCESS_STATE',
551+
payload: {
552+
latestState: state,
553+
accessedByAction: action,
554+
},
555+
})
556+
557+
// dispatch { type: 'STATE_STREAM_TEST' } in Redux DevTools to test
558+
const stateStreamTest = (action$, state$) => compose(
559+
map(accessStateFromArray),
560+
withState(state$),
561+
select('STATE_STREAM_TEST')
562+
)(action$)
563+
564+
export default stateStreamTest
565+
```
566+
567+
---

Diff for: examples/navigation-react-redux/.babelrc

+1-2
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,5 @@
55
}],
66
"stage-3",
77
"react"
8-
],
9-
"plugins": []
8+
]
109
}

Diff for: examples/navigation-react-redux/epics/index.js

+2
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,15 @@ import searchUsers from './searchUsers'
55
import clearSearchResults from './clearSearchResults'
66
import fetchReposByUser from './fetchReposByUser'
77
import adminAccess from './adminAccess'
8+
import stateStreamTest from './stateStreamTest'
89

910
const rootEpic = combineEpics([
1011
searchUsersDebounced,
1112
searchUsers,
1213
clearSearchResults,
1314
fetchReposByUser,
1415
adminAccess,
16+
stateStreamTest,
1517
])
1618

1719
export default rootEpic
+29
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import {
2+
select,
3+
withState,
4+
} from 'redux-most'
5+
// import {
6+
// select,
7+
// withState,
8+
// } from '../../../src'
9+
import {
10+
curriedMap as map,
11+
} from '../utils'
12+
import compose from 'ramda/src/compose'
13+
14+
const accessStateFromArray = ([state, action]) => ({
15+
type: 'ACCESS_STATE',
16+
payload: {
17+
latestState: state,
18+
accessedByAction: action,
19+
},
20+
})
21+
22+
// dispatch { type: 'STATE_STREAM_TEST' } in Redux DevTools to test
23+
const stateStreamTest = (action$, state$) => compose(
24+
map(accessStateFromArray),
25+
withState(state$),
26+
select('STATE_STREAM_TEST')
27+
)(action$)
28+
29+
export default stateStreamTest

Diff for: examples/navigation-react-redux/package.json

+7-7
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,18 @@
2929
"react-router-redux": "^4.0.5",
3030
"redux": "^3.5.2",
3131
"redux-logger": "^3.0.6",
32-
"redux-most": "^0.5.2"
32+
"redux-most": "0.6.0-alpha2"
3333
},
3434
"devDependencies": {
35-
"babel-cli": "^6.11.4",
36-
"babel-core": "^6.11.4",
35+
"babel-cli": "^6.26.0",
36+
"babel-core": "^6.26.0",
3737
"babel-eslint": "^7.1.1",
38-
"babel-loader": "^7.0.0",
39-
"babel-preset-env": "^1.5.0",
38+
"babel-loader": "^7.1.2",
39+
"babel-preset-env": "^1.6.0",
4040
"babel-preset-react": "^6.24.1",
4141
"babel-preset-stage-3": "^6.22.0",
42-
"babel-register": "^6.24.1",
43-
"webpack": "^2.5.1",
42+
"babel-register": "^6.26.0",
43+
"webpack": "^3.5.5",
4444
"webpack-dev-server": "^2.4.5"
4545
}
4646
}

Diff for: examples/navigation-react-redux/store/index.js

+13-4
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,14 @@ import { createStore, applyMiddleware } from 'redux'
22
// Use Ramda's compose instead of Redux's compose,
33
// because we're already using it elsewhere.
44
import compose from 'ramda/src/compose'
5-
import { createEpicMiddleware } from 'redux-most'
6-
// import { createEpicMiddleware } from '../../../src'
5+
import {
6+
createEpicMiddleware,
7+
createStateStreamEnhancer,
8+
} from 'redux-most'
9+
// import {
10+
// createEpicMiddleware,
11+
// createStateStreamEnhancer,
12+
// } from '../../../src'
713
import { createLogger } from 'redux-logger'
814
import { browserHistory } from 'react-router'
915
import { routerMiddleware } from 'react-router-redux'
@@ -20,7 +26,7 @@ const logger = createLogger({
2026

2127
const middleware = [
2228
logger,
23-
epicMiddleware,
29+
// epicMiddleware,
2430
routerMiddleware(browserHistory),
2531
]
2632

@@ -31,7 +37,10 @@ const composeEnhancers =
3137
})
3238
: compose
3339

34-
const storeEnhancers = composeEnhancers(applyMiddleware(...middleware))
40+
const storeEnhancers = composeEnhancers(
41+
createStateStreamEnhancer(epicMiddleware),
42+
applyMiddleware(...middleware)
43+
)
3544

3645
const store = createStore(rootReducer, storeEnhancers)
3746

Diff for: examples/navigation-react-redux/webpack.config.dev.babel.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -38,9 +38,18 @@ const config = {
3838
module: {
3939
rules: [
4040
{
41-
test: /\.(js|jsx)$/,
42-
use: ['babel-loader'],
41+
test: /\.js$/,
42+
loader: 'babel-loader',
4343
exclude: /node_modules/,
44+
query: {
45+
'presets': [
46+
['env', {
47+
'modules': false,
48+
}],
49+
'stage-3',
50+
'react',
51+
],
52+
},
4453
},
4554
],
4655
},

Diff for: examples/navigation-react-redux/webpack.config.prod.babel.js

+11-2
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,18 @@ const config = {
3232
module: {
3333
rules: [
3434
{
35-
test: /\.(js|jsx)$/,
36-
use: ['babel-loader'],
35+
test: /\.js$/,
36+
loader: 'babel-loader',
3737
exclude: /node_modules/,
38+
query: {
39+
'presets': [
40+
['env', {
41+
'modules': false,
42+
}],
43+
'stage-3',
44+
'react',
45+
],
46+
},
3847
},
3948
],
4049
},

0 commit comments

Comments
 (0)