Package Exports
- redux-most
This package does not declare an exports field, so the exports above have been automatically detected and optimized by JSPM instead. If any package subpath is missing, it is recommended to post an issue to the original package (redux-most) to support the "exports" field. If that is not possible, create a JSPM override to customize the exports field for this package.
Readme
redux-most
Most.js based middleware for Redux.
Handle async actions with monadic streams & reactive programming.
Install
With yarn (recommended):
yarn add redux-mostor with npm:
npm install --save redux-mostAdditionally, make sure the peer dependencies, redux and most, are also installed.
Background
redux-most is based on redux-observable.
It uses the same pattern/concept of "epics"
without requiring RxJS 5 as a peer dependency.
Although redux-observable does provide capability for using other stream libraries via adapters,
redux-most allows you to bypass needing to install both RxJS 5 and most. I prefer most for
working with observables and would rather have minimal dependencies. So, I wrote
this middleware primarily for my own use.
Please, see redux-observable's documentation
for details on usage.
Why most over RxJS?
RxJS 5 is great. It's quite a bit faster than RxJS 4, and Rx, in general, is a
very useful tool which happens to exist across many different languages.
Learning it is definitely a good idea. However, most is significantly smaller,
less complicated, and faster than RxJS 5. I prefer its more minimal set of
operators and its focus on performance. Also, like Ramda
or lodash/fp, most
supports a functional API in which the data collection (a stream, rather than
an array, in this case) gets passed in last. This is important, because it
allows you to use functional programming techniques like currying & partial
application, which you can't do with RxJS without writing your own wrapper
functions, because it only offers an OOP/fluent/chaining style API.
Why integrate most/RxJS with redux instead of recreating it with streams?
It's true that it's quite easy to implement the core ideas of Redux with
observables using the scan operator. (See my inferno-most-fp-demo
for an example.) However, the Redux DevTools
provide what is arguably the nicest developer tooling experience currently available
in the JavaScript ecosystem. Therefore, it is huge to be able to maintain it as an asset
while still reaping the benefits of reactive programming with streams. Purists, those who
are very experienced with working with observables, and those working on smaller apps
may not care as much about taking advantage of that tooling as using an elegant
streams-only based solution, and that's fine. The important thing is having a choice.
Why redux-most or redux-observable over redux-saga?
redux-saga is nice. It's a sophisticated approach to handling asynchronous
actions with Redux and can handle very complicated tasks with ease. However,
due to generators being pull-based, it is much more imperative in nature. I
simply prefer the more declarative style of push-based streams & reactive
programming.
Differences between redux-most & redux-observable
I chose not to extend the Observable/Stream type with a custom ActionsObservable
type. So, when working with redux-most, you will be working with normal most
streams without any special extension methods. However, I have offered something
similar to redux-observable's ofType operator in redux-most with the
select and selectArray helper functions.
Like ofType, select and selectArray are convenience utilities for filtering
actions by a specific type or types. In redux-observable, ofType can optionally take multiple
action types to filter on. In redux-most, we want to be more explicit, as it is generally a good
practice in functional programming to prefer a known number of arguments over a variable amount
of arguments. Therefore, select is used when we want to filter by a single action type, and
selectArray is used when we want to filter by multiple action types (via an array) simultaneously.
Additionally, to better align with the most API, and because these fucntions take a known number
of arguments, select & selectArray are curried, which allows them be used in either a
fluent style or a more functional style which enables the use of further currying, partial
application, & functional composition.
To use the fluent style, just use most's thru operator to pass the stream
through to select/selectArray as the 2nd argument.
action$.thru(select(ActionTypes.SOME_ACTION_TYPE))
action$.thru(selectArray([ActionTypes.SOME_ACTION_TYPE, ActionTypes.SOME_OTHER_ACTION_TYPE]))Otherwise, simply directly pass the stream as the 2nd argument.
select(ActionTypes.SOME_ACTION_TYPE, action$)
selectArray([ActionTypes.SOME_ACTION_TYPE, ActionTypes.SOME_OTHER_ACTION_TYPE], action$)API Reference
createEpicMiddleware (rootEpic)
createEpicMiddleware(rootEpic) is used to create an instance of the actual redux-most middleware.
You provide a single, root Epic.
Arguments
rootEpic(Epic): The root Epic.
Returns
(MiddlewareAPI): An instance of the redux-most middleware.
Example
// redux/configureStore.js
import { createStore, applyMiddleware, compose } from 'redux'
import { createEpicMiddleware } from 'redux-most'
import { rootEpic, rootReducer } from './modules/root'
const epicMiddleware = createEpicMiddleware(rootEpic)
export default function configureStore() {
const store = createStore(
rootReducer,
applyMiddleware(epicMiddleware)
)
return store
}combineEpics (...epics)
combineEpics(), as the name suggests, allows you to take multiple epics and combine them into a single one.
Arguments
...epics(Epic[]): The epics to combine.
Returns
(Epic): An Epic that merges the output of every Epic provided and passes along the redux store as arguments.
Example
// epics/index.js
import { combineEpics } from 'redux-most'
import pingEpic from './ping'
import fetchUserEpic from './fetchUser'
export default combineEpics(
pingEpic,
fetchUserEpic
)EpicMiddleware
An instance of the redux-most middleware.
To create it, pass your root Epic to createEpicMiddleware.
Methods
replaceEpic (nextEpic)
Replaces the epic currently used by the middleware.
It is an advanced API. You might need this if your app implements code splitting and you want to load some of the epics dynamically or you're using hot reloading.
Arguments
nextEpic(Epic): The next epic for the middleware to use.
select (actionType, stream)
A helper function for filtering the stream of actions by a single action type.
Arguments
actionType(String): The type of action to filter by.stream(Stream): The stream of actions you are filtering. Ex:actions$.
The select operator is curried, allowing you to use a fluent or functional style.
Examples
// fluent style
import * as ActionTypes from '../ActionTypes'
import { clearSearchResults } from '../actions'
import { select } from 'redux-most'
const clear = action$ =>
action$.thru(select(ActionTypes.SEARCHED_USERS_DEBOUNCED))
.filter(action => !action.payload.query)
.map(clearSearchResults)
export default clear// functional style
import * as ActionTypes from '../ActionTypes'
import { clearSearchResults } from '../actions'
import { select } from 'redux-most'
const clear = action$ => {
const search$ = select(ActionTypes.SEARCHED_USERS_DEBOUNCED, action$)
const emptySearch$ = filter(action => !action.payload.query, search$)
return map(clearSearchResults, emptySearch$)
}
export default clearselectArray (actionTypes, stream)
A helper function for filtering the stream of actions by an array of action types.
Arguments
actionTypes(Array): An array of action types to filter by.stream(Stream): The stream of actions you are filtering. Ex:actions$.
The selectArray operator is curried, allowing you to use a fluent or functional style.
Examples
// fluent style
import * as ActionTypes from '../ActionTypes'
import { clearSearchResults } from '../actions'
import { selectArray } from 'redux-most'
const clear = action$ =>
action$.thru(selectArray([ActionTypes.SEARCHED_USERS, ActionTypes.SEARCHED_USERS_DEBOUNCED]))
.filter(action => !action.payload.query)
.map(clearSearchResults)
export default clear// functional style
import * as ActionTypes from '../ActionTypes'
import { clearSearchResults } from '../actions'
import { selectArray } from 'redux-most'
const clear = action$ => {
const search$ = selectArray([ActionTypes.SEARCHED_USERS, ActionTypes.SEARCHED_USERS_DEBOUNCED], action$)
const emptySearch$ = filter(action => !action.payload.query, search$)
return map(clearSearchResults, emptySearch$)
}
export default clear