Gathering detailed insights and metrics for redux-most
Gathering detailed insights and metrics for redux-most
Gathering detailed insights and metrics for redux-most
Gathering detailed insights and metrics for redux-most
refract-redux-most
Refract bindings for Redux with Most: harness the power of reactive programming to supercharge your components!
redux-most-factory
I am tired of managing actions, actions creators and reducers in Redux. Luckily, I am not alone, so there are many cool libraries simplifying that. I am also tired of managing actions, actions creators and epics in [`redux-most`](https://github.com/joshbu
redux-observable-adapter-most
Use most.js streams with redux-observable
@team-griffin/redux-most
Most.js based middleware for Redux. Handle async actions with monadic streams and reactive programming.
npm install redux-most
Typescript
Module System
Node Version
NPM Version
61.2
Supply Chain
98.5
Quality
75.6
Maintenance
100
Vulnerability
100
License
JavaScript (100%)
Total Downloads
236,324
Last Day
9
Last Week
382
Last Month
2,086
Last Year
49,260
139 Stars
185 Commits
14 Forks
9 Watching
8 Branches
3 Contributors
Minified
Minified + Gzipped
Latest Version
0.8.0
Package Id
redux-most@0.8.0
Size
19.32 kB
NPM Version
3.10.10
Node Version
6.12.1
Publised On
27 Dec 2018
Cumulative downloads
Total Downloads
Last day
28.6%
9
Compared to previous day
Last week
-3%
382
Compared to previous week
Last month
-27.8%
2,086
Compared to previous month
Last year
407.3%
49,260
Compared to previous year
1
25
Most.js based middleware for Redux.
Handle async actions with monadic streams & reactive programming.
With yarn (recommended):
1yarn add redux-most
or with npm:
1npm install --save redux-most
Additionally, make sure the peer dependencies, redux
and most
, are also installed.
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.
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.
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.
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.
redux-most
& redux-observable
Summary
redux-most
is only intended to be used with Most
.redux-most
offers 2 separate APIs: a redux-observable
-like API, where Epics
get passed an action stream & a store middleware object containing dispatch
& getState
methods, and a stricter, more declarative API, where Epics get passed an action stream & a state stream.combineEpics
takes in an array of epics instead of multiple arguments.Most
streams are used instead of a custom Observable extension.select
and selectArray
are available instead of the variadic ofType
.Further Elaboration:
As the name implies, redux-most
does not offer adapters for use with other reactive
programming libraries that implement the Observable type. It's merely an implementation of
redux-observable
's "Epic" pattern exclusively intended for use with Most
. Most
is arguably
the fastest, simplest, most functional, & most elegant reactive programming library in the
JavaScript ecosystem right now, and Most 2.0
will be even better, as it will feature an
auto-curried API like lodash/fp
and ramda
, but for working with streams instead of arrays.
For a preview of what's to come, check out what's going on here.
Initially, redux-most
offered the same API as redux-observable
, where Epics received an action
stream & a store middleware object containing dispatch
& getState
methods. However, it now offers
both that API and another stricter, more declarative API which eliminates the use of dispatch
&
getState
. The reason for this is that I rarely found myself using the imperative dispatch
method. It's not really needed, because you can use switch
, merge
, mergeArray
, etc. to send
multiple actions through your outgoing stream. This is nice, because it allows you to stay locked into
the declarative programming style the entire time.
However, using getState
was still required in epics that needed access to the current state. I
wanted a nice, convenient way to access the current state, just like I had for dispatching actions.
So, I created an alternate API where Epics receive a stream of state changes rather than the
{ dispatch, getState }
object. This state stream, combined with the new withState
utility function,
let's you use streams for both dispatching actions & accessing the current state, allowing you to stay
focused & in the zone (the reactive programming mindset).
Moving on, whereas comebineEpics
is variadic in redux-observable
, it's unary in redux-most
. It
takes in only one argument, an array of epics, instead of individual epics getting passed in as separate
arguments.
As for streams, I chose not to extend the Observable
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 functions take a known number
of arguments, select
& selectArray
are curried, which allows them to 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.
1// Fluent style
2const filteredAction$ = action$.thru(select(SOME_ACTION_TYPE))
3const filteredActions$ = action$.thru(selectArray([SOME_ACTION_TYPE, SOME_OTHER_ACTION_TYPE]))
Otherwise, simply directly pass the stream as the 2nd argument.
1// Functional style 2const filteredAction$ = select(SOME_ACTION_TYPE, action$) 3const filteredActions$ = selectArray([SOME_ACTION_TYPE, SOME_OTHER_ACTION_TYPE], action$)
Alternatively, you can delay passing the 2nd argument while defining functional pipelines
via functional composition by using the compose
or pipe
functions from your favorite FP library,
like ramda
or lodash/fp
. Again, this is because select
& selectArray
are auto-curried. Being
able to program in this very functional & Pointfree style is one of the main reasons why someone
might prefer using redux-most over redux-observable.
1// Functional & Pointfree style using currying & functional composition 2import { compose, curry, pipe } from 'ramda' 3import { debounce, filter, map } from 'most' 4 5// NOTE: Most 2.0 will feature auto-curried functions, but right now we must curry them manually. 6const curriedDebounce = curry(debounce) 7const curriedFilter = curry(filter) 8const curriedMap = curry(map) 9 10// someEpic is a new function which is still awaiting one argument, the action$ 11const someEpic = compose( 12 curriedMap(someFunction), 13 curriedDebounce(800), 14 select(SOME_ACTION_TYPE) 15) 16 17// someOtherEpic is a new function which is still awaiting one argument, the action$ 18// pipe is the same as compose, but read from left-to-right rather than right-to-left. 19const someOtherEpic = pipe( 20 selectArray([SOME_ACTION_TYPE, SOME_OTHER_ACTION_TYPE]), 21 curriedFilter(somePredicate), 22 curriedMap(someFunction) 23)
createEpicMiddleware (rootEpic)
createEpicMiddleware
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
1// redux/configureStore.js 2 3import { createStore, applyMiddleware, compose } from 'redux' 4import { createEpicMiddleware } from 'redux-most' 5import { rootEpic, rootReducer } from './modules/root' 6 7const epicMiddleware = createEpicMiddleware(rootEpic) 8 9export default function configureStore() { 10 const store = createStore( 11 rootReducer, 12 applyMiddleware(epicMiddleware) 13 ) 14 15 return store 16}
createStateStreamEnhancer (epicMiddleware)
createStateStreamEnhancer
is used to access redux-most
's alternate API, which passes
Epics
a state stream (Ex: state$
) instead of the { dispatch, getState }
store
MiddlewareAPI
object. You must provide an instance of the EpicMiddleware
, and the
resulting function must be applied AFTER using redux
's applyMiddleware
if also using
other middleware.
Arguments
rootEpic
(Epic
): The root Epic.Returns
(MiddlewareAPI
): An enhanced instance of the redux-most
middleware, exposing a stream
of state change values.
Example
1import { createStore, applyMiddleware } from 'redux' 2import { 3 createEpicMiddleware, 4 createStateStreamEnhancer, 5} from 'redux-most' 6import rootEpic from '../epics' 7 8const epicMiddleware = createEpicMiddleware(rootEpic) 9const middleware = [...] // other middleware here 10const storeEnhancers = compose( 11 createStateStreamEnhancer(epicMiddleware), 12 applyMiddleware(...middleware) 13) 14 15const store = createStore(rootReducer, storeEnhancers)
combineEpics (epicsArray)
combineEpics
, as the name suggests, allows you to pass in an array of epics and combine them into a single one.
Arguments
epicsArray
(Epic[]
): The array of epics
to combine into one root epic.Returns
(Epic
): An Epic that merges the output of every Epic provided and passes along the redux store as arguments.
Example
1// epics/index.js 2 3import { combineEpics } from 'redux-most' 4import searchUsersDebounced from './searchUsersDebounced' 5import searchUsers from './searchUsers' 6import clearSearchResults from './clearSearchResults' 7import fetchReposByUser from './fetchReposByUser' 8import adminAccess from './adminAccess' 9 10const rootEpic = combineEpics([ 11 searchUsersDebounced, 12 searchUsers, 13 clearSearchResults, 14 fetchReposByUser, 15 adminAccess, 16]) 17 18export default rootEpic 19
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.
Example
1 2import { createEpicMiddleware } from 'redux-most' 3import rootEpic from '../epics' 4 5... 6 7const epicMiddleware = createEpicMiddleware(rootEpic) 8 9... 10 11// hot reload epics 12const replaceRootEpic = () => { 13 import('../epics').then( 14 ({ default: nextRootEpic }) => { epicMiddleware.replaceEpic(nextRootEpic) } 15 ) 16} 17 18if (module.hot) { 19 module.hot.accept('../epics', replaceRootEpic) 20}
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$
.Returns
(Stream): A new, filtered stream holding only the actions corresponding to the action
type passed to select
.
The select
operator is curried, allowing you to use a fluent or functional style.
Examples
1// Fluent style 2 3import { SEARCHED_USERS_DEBOUNCED } from '../constants/ActionTypes' 4import { clearSearchResults } from '../actions' 5import { select } from 'redux-most' 6 7const whereEmpty = ({ payload: { query } }) => !query 8 9const clear = action$ => 10 action$.thru(select(SEARCHED_USERS_DEBOUNCED)) 11 .filter(whereEmpty) 12 .map(clearSearchResults) 13 14export default clear
1// Functional style 2 3import { SEARCHED_USERS_DEBOUNCED } from '../constants/ActionTypes' 4import { clearSearchResults } from '../actions' 5import { select } from 'redux-most' 6 7const whereEmpty = ({ payload: { query } }) => !query 8 9const clear = action$ => { 10 const search$ = select(SEARCHED_USERS_DEBOUNCED, action$) 11 const emptySearch$ = filter(whereEmpty, search$) 12 return map(clearSearchResults, emptySearch$) 13} 14 15export default clear
1// Functional & Pointfree style using functional composition 2 3import { SEARCHED_USERS_DEBOUNCED } from '../constants/ActionTypes' 4import { clearSearchResults } from '../actions' 5import { select } from 'redux-most' 6import { 7 curriedFilter as filter, 8 curriedMap as map, 9} from '../utils' 10import { compose } from 'ramda' 11 12const whereEmpty = ({ payload: { query } }) => !query 13 14const clear = compose( 15 map(clearSearchResults), 16 filter(whereEmpty), 17 select(SEARCHED_USERS_DEBOUNCED) 18) 19 20export default clear
selectArray (actionTypes, stream)
A helper function for filtering the stream of actions by an array of action types.
Arguments
actionTypes
(string[]
): An array of action types to filter by.stream
(Stream
): The stream of actions you are filtering. Ex: actions$
.Returns
(Stream): A new, filtered stream holding only the actions corresponding to the action
types passed to selectArray
.
The selectArray
operator is curried, allowing you to use a fluent or functional style.
Examples
1// Fluent style 2 3import { 4 SEARCHED_USERS, 5 SEARCHED_USERS_DEBOUNCED, 6} from '../constants/ActionTypes' 7import { clearSearchResults } from '../actions' 8import { selectArray } from 'redux-most' 9 10const whereEmpty = ({ payload: { query } }) => !query 11 12const clear = action$ => 13 action$.thru(selectArray([ 14 SEARCHED_USERS, 15 SEARCHED_USERS_DEBOUNCED, 16 ])) 17 .filter(whereEmpty) 18 .map(clearSearchResults) 19 20export default clear
1// Functional style 2 3import { 4 SEARCHED_USERS, 5 SEARCHED_USERS_DEBOUNCED, 6} from '../constants/ActionTypes' 7import { clearSearchResults } from '../actions' 8import { selectArray } from 'redux-most' 9 10const whereEmpty = ({ payload: { query } }) => !query 11 12const clear = action$ => { 13 const search$ = selectArray([ 14 SEARCHED_USERS, 15 SEARCHED_USERS_DEBOUNCED, 16 ], action$) 17 const emptySearch$ = filter(whereEmpty, search$) 18 return map(clearSearchResults, emptySearch$) 19} 20 21export default clear
1// Functional & Pointfree style using functional composition 2 3import { 4 SEARCHED_USERS, 5 SEARCHED_USERS_DEBOUNCED, 6} from '../constants/ActionTypes' 7import { clearSearchResults } from '../actions' 8import { selectArray } from 'redux-most' 9import { 10 curriedFilter as filter, 11 curriedMap as map, 12} from '../utils' 13import { compose } from 'ramda' 14 15const whereEmpty = ({ payload: { query } }) => !query 16 17const clear = compose( 18 map(clearSearchResults), 19 filter(whereEmpty), 20 selectArray([ 21 SEARCHED_USERS, 22 SEARCHED_USERS_DEBOUNCED, 23 ]) 24) 25 26export default clear
withState (stateStream, actionStream)
A utility function for use with redux-most
's optional state stream API. This
provides a convenient way to sample
the latest state change value. Note:
accessing the alternate API requires using createStateStreamEnhancer
.
Arguments
stateStream
(Stream
): The state stream provided by redux-most
's alternate API.actionStream
(Stream
): The filtered stream of action events used to trigger
sampling of the latest state. (Ex: actions$
).Returns
([state, action]
): An Array of length 2 (or Tuple) containing the latest
state value at index 0 and the latest action of the filtered action stream at index 1.
withState
is curried, allowing you to pass in the state stream & action stream
together, at the same time, or separately, delaying passing in the action stream.
This provides the user extra flexibility, allowing it to easily be used within
functional composition pipelines.
Examples
1import { select, withState } from 'redux-most' 2import { curriedMap as map } from '../utils' 3import compose from 'ramda/src/compose' 4 5const accessStateFromArray = ([state, action]) => ({ 6 type: 'ACCESS_STATE', 7 payload: { 8 latestState: state, 9 accessedByAction: action, 10 }, 11}) 12 13// dispatch { type: 'STATE_STREAM_TEST' } in Redux DevTools to test 14const stateStreamTest = (action$, state$) => compose( 15 map(accessStateFromArray), 16 withState(state$), 17 select('STATE_STREAM_TEST') 18)(action$) 19 20export default stateStreamTest
No vulnerabilities found.
Reason
no binaries found in the repo
Reason
license file detected
Details
Reason
Found 1/28 approved changesets -- score normalized to 0
Reason
0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0
Reason
no effort to earn an OpenSSF best practices badge detected
Reason
security policy file not detected
Details
Reason
project is not fuzzed
Details
Reason
branch protection not enabled on development/release branches
Details
Reason
SAST tool is not run on all commits -- score normalized to 0
Details
Reason
129 existing vulnerabilities detected
Details
Score
Last Scanned on 2024-12-16
The Open Source Security Foundation is a cross-industry collaboration to improve the security of open source software (OSS). The Scorecard provides security health metrics for open source projects.
Learn More