A collection of Redux libraries for modular applications, such as those utilizing React-union or other SPA-in-CMS solutions. They allow you to easily:
- tie reducers/epics/middleware to your widgets and handle their lifecycle, i.e. injection and ejection, automatically.
- isolate your widgets' Redux state while still allowing them to communicate with one another.
They offer seamless integration with React and React-union. See the packages and the FAQ for more details!
- actions – Functions for creating FSA-compliant action creators and reducers.
- reducers – Redux store enhancer for asynchronous injection of reducers.
- reducers-react – React bindings for the reducers package.
- epics – Redux store enhancer for asynchronous injection of epics.
- epics-react – React bindings for the epics package.
- stream-creators – Collection of stream creators for the epics package.
- middleware – Redux store enhancer for asynchronous injection of middleware.
- middleware-react – React bindings for the middleware package.
- thunk – Clone of the awesome (and extremely simple) Redux Thunk library.
The following packages are used internally and are not meant to be used directly in your application code. You might want to use them for some custom features or more advanced integration, though.
- namespaces – Logic for associating Redux actions with a namespace.
- injectors – Internal reusable logic for the injection mechanism itself.
- injectors-react – Core React injector functionality.
- utils – Various utility functions not tied to the @redux-tools domain.
- utils-react – Various React-specific utility functions not tied to the @redux-tools domain.
This example uses React, but the tools are platform agnostic. You should already know the basics of Redux before using this library!
We will be creating a simple click counter, which will have its state stored in Redux. The catch is that we should have a variable number of isolated counters on a single page!
// duck.js
import { makeActionTypes, makeConstantActionCreator, makeReducer } from '@redux-tools/actions';
export const ActionTypes = makeActionTypes('duck', ['INCREMENT']);
export const increment = makeConstantActionCreator(ActionTypes.INCREMENT);
export default makeReducer([[ActionTypes.INCREMENT, count => count + 1]], 0);
// Counter.js
import React from 'react';
import { o } from 'ramda';
import { withReducers, namespacedConnect } from '@redux-tools/reducers-react';
import countReducer, { increment } from './duck';
const Counter = ({ count, increment }) => <button onClick={increment}>{count}</button>;
const mapStateToProps = namespacedState => ({ count: namespacedState.count });
const mapDispatchToProps = { increment };
const enhance = o(
withReducers({ count: countReducer }), // This handles the asynchronous reducer injection.
namespacedConnect(mapStateToProps, mapDispatchToProps) // This is just like `connect` from react-redux
);
export default enhance(Counter);
// index.js
import React from 'react';
import { render } from 'react-dom';
import { createStore } from 'redux';
import { makeEnhancer as makeReducersEnhancer } from '@redux-tools/reducers';
import { Provider } from '@redux-tools/reducers-react';
import { identity } from 'ramda';
import Counter from './Counter';
// `makeReducersEnhancer()` is an enhancer just like `applyMiddleware()`, so they're composable!
const store = createStore(identity, makeReducersEnhancer());
render(
<Provider store={store}>
<Counter namespace="foo" />
<Counter namespace="bar" />
<Counter namespace="baz" />
</Provider>,
document.getElementById('root')
);
And that's it! The state structure will look like this:
{
"namespaces": {
"foo": {
"count": 0
},
"bar": {
"count": 0
},
"baz": {
"count": 0
}
}
}