Skip to content

Instantly share code, notes, and snippets.

@amsterdamharu
Last active March 17, 2021 19:12
Show Gist options
  • Save amsterdamharu/2ad7e85529df42b12ae32ba4edd6e368 to your computer and use it in GitHub Desktop.
Save amsterdamharu/2ad7e85529df42b12ae32ba4edd6e368 to your computer and use it in GitHub Desktop.
useReducer with middleware
import React from 'react';
import ReactDOM from 'react-dom';
//for Stack overflow snippet that doesn't allow imports
const { useRef, useState } = React;
const compose = (...fns) =>
fns.reduce((result, fn) => (...args) =>
fn(result(...args))
);
const mw = () => (next) => (action) => next(action);
const createMiddleware = (...middlewareFunctions) => (
store
) =>
compose(
...middlewareFunctions
.concat(mw)
.reverse()
.map((fn) => fn(store))
);
const useMiddlewareReducer = (
reducer,
initialState,
middleware = () => (b) => (c) => b(c)
) => {
const stateContainer = useRef(initialState);
const [state, setState] = useState(initialState);
const dispatch = (action) => {
const next = (action) => {
stateContainer.current = reducer(
stateContainer.current,
action
);
return setState(stateContainer.current);
};
const store = {
dispatch,
getState: () => stateContainer.current,
};
return middleware(store)(next)(action);
};
return [state, dispatch];
};
//middleware
const thunkMiddleWare = ({ getState, dispatch }) => (
next
) => (action) =>
typeof action === 'function'
? action(dispatch, getState)
: next(action);
const logMiddleware = ({ getState }) => (next) => (
action
) => {
console.log('in log middleware', action, getState());
Promise.resolve().then(() =>
console.log('after action:', action.type, getState())
);
return next(action);
};
const init = { value: 'A' };
const TOGGLE = 'TOGGLE';
const later = () =>
new Promise((r) => setTimeout(() => r(), 500));
const otherThunk = () => (dispatch, getState) => {
dispatch({ type: 'not relevant action form otherTunk' });
console.log('in other thunk, state is:', getState());
//note that I am returning a promise
return later();
};
window.later = later;
const thunkToggle = () => (dispatch, getState) => {
//dispatching other thunk and waiting for it to finish
otherThunk()(dispatch, getState).then(() =>
dispatch({ type: TOGGLE })
);
};
const reducer = (state, { type }) => {
console.log(`in reducer action type: ${type}`);
//toggle state.value between A and B
if (type === TOGGLE) {
return { value: state.value === 'A' ? 'B' : 'A' };
}
return state;
};
const middleware = createMiddleware(
thunkMiddleWare,
logMiddleware
);
const App = () => {
const [state, dispatch] = useMiddlewareReducer(
reducer,
init,
middleware
);
return (
<div>
<button onClick={() => dispatch(thunkToggle())}>
toggle
</button>
<pre>{JSON.stringify(state, undefined, 2)}</pre>
</div>
);
};
ReactDOM.render(<App />, document.getElementById('root'));
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment