mirror of
https://github.com/streamich/react-use.git
synced 2026-01-18 14:06:52 +00:00
Merge pull request #315 from streamich/createReducer
This commit is contained in:
commit
c3ecced951
@ -90,6 +90,8 @@
|
||||
"react-hooks-testing-library": "0.4.1",
|
||||
"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.5.2",
|
||||
"semantic-release": "15.13.12",
|
||||
|
||||
55
src/__stories__/createReducer.story.tsx
Normal file
55
src/__stories__/createReducer.story.tsx
Normal file
@ -0,0 +1,55 @@
|
||||
import { storiesOf } from '@storybook/react';
|
||||
import * as React from 'react';
|
||||
import logger from 'redux-logger';
|
||||
import thunk from 'redux-thunk';
|
||||
|
||||
import { createReducer } from '..';
|
||||
|
||||
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 dispatch2 => {
|
||||
dispatch2({ type: 'increment' });
|
||||
|
||||
setTimeout(() => {
|
||||
dispatch2({ 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 />);
|
||||
36
src/createReducer.ts
Normal file
36
src/createReducer.ts
Normal file
@ -0,0 +1,36 @@
|
||||
import { useMemo, useRef, useState } from 'react';
|
||||
|
||||
function composeMiddleware(chain) {
|
||||
return (context, dispatch) => {
|
||||
return chain.reduceRight((res, middleware) => {
|
||||
return middleware(context)(res);
|
||||
}, dispatch);
|
||||
};
|
||||
}
|
||||
|
||||
const createReducer = (...middlewares) => (reducer, initialState, initializer = value => value) => {
|
||||
const ref = useRef(initializer(initialState));
|
||||
const [, setState] = useState(ref.current);
|
||||
let middlewareDispatch = (_ = {}) => {
|
||||
throw new Error(
|
||||
'Dispatching while constructing your middleware is not allowed. ' +
|
||||
'Other middleware would not be applied to this dispatch.'
|
||||
);
|
||||
};
|
||||
const dispatch = action => {
|
||||
ref.current = reducer(ref.current, action);
|
||||
setState(ref.current);
|
||||
return action;
|
||||
};
|
||||
const composedMiddleware = useMemo(() => {
|
||||
return composeMiddleware(middlewares);
|
||||
}, middlewares);
|
||||
const middlewareAPI = {
|
||||
getState: () => ref.current,
|
||||
dispatch: (...args) => middlewareDispatch(...args),
|
||||
};
|
||||
middlewareDispatch = composedMiddleware(middlewareAPI, dispatch);
|
||||
return [ref.current, middlewareDispatch];
|
||||
};
|
||||
|
||||
export default createReducer;
|
||||
@ -1,4 +1,5 @@
|
||||
import createMemo from './createMemo';
|
||||
import createReducer from './createReducer';
|
||||
import useAsync from './useAsync';
|
||||
import useAsyncFn from './useAsyncFn';
|
||||
import useAsyncRetry from './useAsyncRetry';
|
||||
@ -73,6 +74,7 @@ import useWindowSize from './useWindowSize';
|
||||
|
||||
export {
|
||||
createMemo,
|
||||
createReducer,
|
||||
useAsync,
|
||||
useAsyncFn,
|
||||
useAsyncRetry,
|
||||
|
||||
17
yarn.lock
17
yarn.lock
@ -5267,6 +5267,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"
|
||||
@ -11894,6 +11899,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"
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user