UNPKG

redux-haiku

Version:

> A saga is long and rambles on. > A haiku goes to the point. > Change!

189 lines (153 loc) 4.62 kB
import example from 'washington' import { deepEqual } from 'assert' import { connect, getDiff } from './src/index' import { createStore, compose } from 'redux' import { execSync } from 'child_process' type Action = { type: string, payload: any } type Item = { key: string, saved: boolean } type State = { items: Array<Item> } example(`save an item removed after it's is added`, (done) => { /* ACTIONS */ const addItemActionCreator = (text): Action => ({ type: 'ADD_ITEM', payload: { key: text, saved: false } }) const itemSavedActionCreator = (key): Action => ({ type: 'ITEM_SAVED', payload: key }) /* EXTERNAL LIBRARY FOR A SIDE EFFECT */ const asyncSaveFunction = (item, onSaved) => { // We fake the asynchronous save request, which could for example // be calling a REST endpoint setTimeout(() => { console.log(`item('${item.key}') is saved now`) onSaved(item) }, 20) } /* REDUCER */ const initialState: State = { items: [] } const reducer = (state: State, action: Action): State => { switch (action.type) { case 'ADD_ITEM': return { items: [ ...state.items, action.payload ] } case 'ITEM_SAVED': return { items: [ ...state.items.map((item) => item.key === action.payload ? { ...item, saved: true } : item ) ] } default: return state } } /* SELECTORS */ const getItemKeysSelector = (state) => state && state.items.map(({ key }) => ({ key })) /* SUBSCRIBER *new shiny thing* * you can think about this as something like a react-redux container * for arbitrary asynchronous side effects */ const subscriber = ({ items, onSaved }) => items.map((item) => asyncSaveFunction(item, onSaved)) const mapStateToProps = (state, prevState) => { const newItemKeys = getDiff(getItemKeysSelector)(prevState, state).after return newItemKeys && { items: newItemKeys.map(({ key }) => state.items.find((item) => item.key === key) ) } } const mapDispatchToProps = (dispatch, getState) => ({ onSaved: (item) => compose(dispatch, itemSavedActionCreator)(item.key) }) const connectedSubscriber = connect( mapStateToProps, mapDispatchToProps )(subscriber) /* STORE */ const store = createStore(reducer, initialState) // Connect the subscriber connectedSubscriber(store) // Start the thing by creating two items store.dispatch(addItemActionCreator('hello world of subscribers')) store.dispatch(addItemActionCreator('hola mundo de los subscribers')) /* ASSERTION */ // Let's wait a couple of milliseconds for the asynchronous operations to // complete setTimeout(() => { deepEqual( store.getState().items, [ { key: 'hello world of subscribers', saved: true }, { key: 'hola mundo de los subscribers', saved: true } ] ) done() }, 30) }) example(`it will show an error if you try to dispatch synchronously`, (done) => { const addItem = (key) => ({ type: 'ADD_ITEM', payload: { key } }) const compactItems = () => ({ type: 'COMPACT_ITEMS' }) const initialState = { items: [], compacted: false } const reducer = (state, action) => { switch (action.type) { case 'ADD_ITEM': return { ...state, items: [ ...state.items, action.payload ] } case 'COMPACT_ITEMS': return { ...state, compected: true } default: return state } } const store = createStore(reducer, initialState) const faultySubscriber = ({ items, onThirdItem }) => { if (items.length > 2) { onThirdItem() } } const mapStateToProps = (state) => state const mapDispatchToProps = (dispatch) => ({ onThirdItem: compose(dispatch, compactItems) }) const connectedFaultySubscriber = connect(mapStateToProps, mapDispatchToProps)(faultySubscriber) connectedFaultySubscriber(store) store.dispatch(addItem('first')) store.dispatch(addItem('second')) try { store.dispatch(addItem('third')) } catch (e) { done(e.message, `Dispatching synchronously in a Subscriber is forbidden. Callbacks provided to Subscribers are meant to be used by asynchronous side effects as a way to trigger actions back into the store. Operations on the store to be done as a consequence of a particular state change should be done in reducers or selectors instead.`) } }) example.go()