A slightly opiniated redux framework with the goal to reduce boilerplate and to modularize application logic.
// ./bits/myTestBit.js
// A unique name for the bit.
export const name = 'myTestBit';
// The initial state of the bit.
export const state = {
foo: true,
bar: 'someText',
};
// Simplified action creators.
// Just return the payload for redux action.
export const actions = {
fooAction: foo => !foo,
barAction: bar => ({ bar: bar.toUpperCase() }),
};
// Simplified reducers.
// Just mutate the draft state, the original will be left untouched.
// Instead of providing the full action only the payload is provided.
// Each action must have one matching reducer with the same name.
export const reducers = {
fooAction: (draft, payload) => {
draft.foo = payload;
},
barAction: (draft, { bar }) => {
draft.bar = bar;
},
};
// ./bits/myTestBit.js
// ...
// Selectors
// For memoized or more complex selectors you might want to use reselect.
export const selectors = {
fooBar: state => state.foo ? state.bar.toUpperCase() : state.bar.toLowerCase(),
};
If the action types are required elsewhere in your application you may export them using an included helper function:
// ./bits/myTestBit.js
import { actionTypes } from 'redux-bits';
// ...
export const types = actionTypes(actions);
Export state container as default:
// ./bits/myTestBit.js
import { createContainer } from 'redux-bits';
// ...
export default createContainer(name, actions, selector /*optional*/);
Use createStore
from redux-bits
to create your redux store instead of using the original
one from redux
.
You may add any third party reducers, middlewares and/or store enhancers.
// store.js
import { createStore } from 'redux-bits';
import * as myTestBit from './bits/myTestBit';
// all options are optional
const config = {
bits: [myTestBit],
reducers: {},
middlewares: [],
enhancers: [],
initialState: {},
};
export default createStore(config);
To use bits with react, export the state container from the bit (see above). The default state container accepts a function as a child that receives the bit's state as parameter:
// ./testComponent.js
import React from 'react'
import MyTestBit from 'bits/myTestBit'
export default () => (
<div>
<MyTestBit>
{({ foo, bar, fooAction }) => (
<p>Foo: {foo}</p>
<p>Bar: {bar}</p>
<a onClick={fooAction}>Foo action</a>
)}
</MyTestBit>
</div>
);
The default state container follows the function as a child pattern.
In addition, redux-bits
provides an alternative render prop Component
pattern,
for those who don't like functions as a child.
Using this approach makes testing the component much easier.
// ./bits/myTestBit.js
import { createContainer } from 'redux-bits';
// ...
export default createComponentContainer(name, actions, selector /*optional*/);
// ./testComponent.js
import React from 'react'
import MyTestBit from 'bits/myTestBit'
export const TestRenderer = ({ foo, bar, fooAction }) => (
<p>Foo: {foo}</p>
<p>Bar: {bar}</p>
<a onClick={fooAction}>Foo action</a>
);
export default () => (
<div>
<MyTestBit Component={TestRenderer} />
</div>
);
Selectors that are defined in the corresponding bit may be used by setting the selector
prop to
the selector's name.
// ./fooComponent.js
import React from 'react'
import MyTestBit from 'bits/myTestBit';
export default () => (
<div>
<MyTestBit selector='fooBar'>
{({ fooBar, fooAction }) => (
<p>Foo: {fooBar}</p>
<a onClick={fooAction}>action creators are still available</a>
)}
</MyTestBit>
</div>
);
You may use any selector function as selector
prop.
In addition to the globalState the bit's state is provided as second parameter.
// ./barComponent.js
import React from 'react'
import MyTestBit from 'bits/myTestBit';
export default () => (
<div>
<MyTestBit selector={(globalState, bitState) => ({
globalValue: globalState.someGlobalValue,
bar: bitState.bar,
})}>
{({ bar, globalValue }) => (
<p>Bar: {bar}</p>
<p>Global value: {globalValue}</p>
)}
</MyTestBit>
</div>
);