Skip to content

Commit

Permalink
createReducer for redux middleware, related #164
Browse files Browse the repository at this point in the history
  • Loading branch information
wardoost committed Apr 16, 2019
1 parent 9c5c289 commit 766ab2f
Show file tree
Hide file tree
Showing 5 changed files with 112 additions and 0 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,8 @@
"react-hooks-testing-library": "0.4.0",
"react-spring": "6.1.10",
"rebound": "0.1.0",
"redux-logger": "^3.0.6",
"redux-thunk": "^2.3.0",
"rimraf": "2.6.3",
"rxjs": "6.4.0",
"semantic-release": "15.13.3",
Expand Down
56 changes: 56 additions & 0 deletions src/__stories__/createReducer.story.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
import { storiesOf } from '@storybook/react';
import * as React from 'react';
import logger from 'redux-logger';
import thunk from 'redux-thunk';

import { createReducer } from '..';
// import ShowDocs from './util/ShowDocs';

const useThunkReducer = createReducer(thunk, logger);

// React useReducer lazy initialization example: https://reactjs.org/docs/hooks-reference.html#lazy-initialization
function init(initialCount) {
return { count: initialCount };
}

function reducer(state, action) {
switch (action.type) {
case 'increment':
return { count: state.count + 1 };
case 'decrement':
return { count: state.count - 1 };
case 'reset':
return init(action.payload);
default:
throw new Error();
}
}

const Demo = ({ initialCount = 1 }) => {
// Action creator to increment count, wait a second and then reset
const addAndReset = React.useCallback(() => {
return dispatch => {
dispatch({ type: 'increment' });

setTimeout(() => {
dispatch({ type: 'reset', payload: initialCount });
}, 1000);
};
}, [initialCount]);

const [state, dispatch] = useThunkReducer(reducer, initialCount, init);

return (
<div>
<pre>{JSON.stringify(state, null, 2)}</pre>
<button onClick={() => dispatch(addAndReset())}>Add and reset</button>
<button onClick={() => dispatch({ type: 'reset', payload: initialCount })}>Reset</button>
<button onClick={() => dispatch({ type: 'increment' })}>+</button>
<button onClick={() => dispatch({ type: 'decrement' })}>-</button>
</div>
);
};

storiesOf('State|createReducer', module)
// .add('Docs', () => <ShowDocs md={require('../../docs/createMemo.md')} />)
.add('Demo', () => <Demo />);
35 changes: 35 additions & 0 deletions src/createReducer.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { useReducer } from 'react';

function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg;
}

if (funcs.length === 1) {
return funcs[0];
}

return funcs.reduce((a, b) => (...args) => a(b(...args)));
}

const createReducer = (...middlewares) => (...args) => {
const [state, dispatch] = useReducer(...args);

let middlewareDispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
);
};

const middlewareAPI = {
getState: () => state,
dispatch: (...args) => middlewareDispatch(...args),
};
const chain = middlewares.map(middleware => middleware(middlewareAPI));
middlewareDispatch = compose(...chain)(dispatch);

return [state, middlewareDispatch];
};

export default createReducer;
2 changes: 2 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import createMemo from './createMemo';
import createReducer from './createReducer';
import useAsync from './useAsync';
import useAsyncFn from './useAsyncFn';
import useAsyncRetry from './useAsyncRetry';
Expand Down Expand Up @@ -72,6 +73,7 @@ import useWindowSize from './useWindowSize';

export {
createMemo,
createReducer,
useAsync,
useAsyncFn,
useAsyncRetry,
Expand Down
17 changes: 17 additions & 0 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -5007,6 +5007,11 @@ dedent@^0.7.0:
resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c"
integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=

deep-diff@^0.3.5:
version "0.3.8"
resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84"
integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ=

deep-extend@^0.6.0:
version "0.6.0"
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
Expand Down Expand Up @@ -11333,6 +11338,18 @@ redeyed@~2.1.0:
dependencies:
esprima "~4.0.0"

redux-logger@^3.0.6:
version "3.0.6"
resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf"
integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8=
dependencies:
deep-diff "^0.3.5"

redux-thunk@^2.3.0:
version "2.3.0"
resolved "https://registry.yarnpkg.com/redux-thunk/-/redux-thunk-2.3.0.tgz#51c2c19a185ed5187aaa9a2d08b666d0d6467622"
integrity sha512-km6dclyFnmcvxhAcrQV2AkZmPQjzPDjgVlQtR0EQjxZPyJ0BnMf3in1ryuR8A2qU0HldVRfxYXbFSKlI3N7Slw==

refractor@^2.4.1:
version "2.7.0"
resolved "https://registry.yarnpkg.com/refractor/-/refractor-2.7.0.tgz#3ed9a96a619e75326a429e644241dea51be070a3"
Expand Down

0 comments on commit 766ab2f

Please sign in to comment.