> npm i --save taverne
Once your barrels are ready, you can instanciate your taverne and dispatch:
import createLaTaverne from 'taverne';
import books from './barrels/books';
import potions from './barrels/potions';
import handcrafts from './barrels/handcrafts';
const barrels = {
books,
potions,
handcrafts
};
const {dispatch, taverne} = createLaTaverne(barrels);
A "Barrel" is an initialState and a list of reactions.
import {ADD_BOOK} from '../actions/shelves';
const addBook = {
on: ADD_BOOK,
reduce: (state, payload) => {
const {book} = payload;
state.entities.push(book);
}
};
export default {
initialState: {entities: []},
reactions: [addBook]
};
reaction will be triggered when an action is dispatched with action.type === on.const doSomethingInThisBarrel = {
on: 'ACTION_TYPE',
perform: (payload, dispatch, getState) => {
/*
Optional sync or async function.
It will be called before `reduce`
When it is done, reduce will receive the returned result in
the `payload` parameter.
You can `dispatch` next steps from here as well
*/
},
reduce: (state, payload) => {
/*
Just update the state with your payload.
Here, `state` is the draftState used by `Immer.produce`
You taverne will then record your next immutable state nested in this barrel.
*/
state.foo = 'bar';
},
after: (payload, dispatch, getState) => {
/*
Optional sync or async function.
It will be called after `reduce`.
payload here is the same one received by reduce
You can `dispatch` next steps from here as well
*/
}
};
reduce is called using Immer, so mutate the state exactly as you would with the draftState parameter in produce.
If you have some business to do before reducing, for example calling an API, use the perform function, either sync or async. Then reduce will be called with the result once it's done.
If you have some business to do after reducing, use the after function, either sync or async.
La Taverne has a context Provider <Taverne> which provides 2 utilities:
pour hook to access your global state anywheredispatch function/* src/app.js */
import React from 'react';
import {render} from 'react-dom';
import {Taverne} from 'taverne/hooks';
render(
<Taverne dispatch={dispatch} taverne={taverne}>
<App id={id} />
</Taverne>,
container
);
/* src/feature/books/container.js */
import {useTaverne} from 'taverne/hooks';
const BooksContainer = props => {
const {dispatch, pour} = useTaverne();
const books = pour('books');
return <BooksComponent books={books} />;
};
See the complete React integration steps here.
You can "pour" specific parts of the state, to allow accurate local rendering from your global app state.
You can create more generic middlewares to operate any actions:
const customMiddlewareCreator = taverne => {
const currentState = taverne.getState();
return {
onCreate: (dispatch, taverne) => {},
onDispatch: (action, dispatch, getState) => {}
};
};
Then instanciate La Taverne with your list of middlewares as 2nd parameter:
const {dispatch, taverne} = createLaTaverne(barrels, [customMiddlewareCreator]);
example: plugging the redux devtools extension with this middleware
import createLaTaverne from 'taverne';
import {createDevtools} from 'taverne/middlewares';
import books from './barrels/books';
const devtools = createDevtools();
const {dispatch, taverne} = createLaTaverne({books}, [devtools]);
For performance, you can use options to hide huge part of the state you don't need to debug
const devtools = createDevtools({
applyStateFiltering: state => ({
...state,
hugeParent: {
...state.hugeParent,
hugeChild: '<HUGE>'
}
})