-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathUseReducerWithMiddleware.js
More file actions
84 lines (78 loc) · 2.49 KB
/
Copy pathUseReducerWithMiddleware.js
File metadata and controls
84 lines (78 loc) · 2.49 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import { useMemo, useReducer, useRef } from "react";
/**
* Same as React's useReducer, but also executes middleware functions on every
* action dispatch.
*
* A middleware is a powerful and flexible function that can
* - dispatch arbitrary actions, e.g. at an interval
* - inspect all the dispatched actions
* - change an action before it reaches the reducer or next middleware
* - stop an action from propagating altogether
*
* The middleware interface is designed to be exactly the same as Redux's
* middlewares - the middleware function must have the following form:
```js
({getState}) => {
return (next) =>
return (action) => {
next(action);
}
}
}
```
* A middleware function must call `next(action)` to propagate the action to
* the final action dispatch and reducer, otherwise the action will be ignored.
*
* Middlewares are executed in reverse order.
*
* @param {Array<Function>} middlewares Middleware functions, executed in reverse order
* @param {Function} reducer Passed to React's useReducer
* @param {any} initOrInitialValue Passed to React's useReducer
* @param {any} init Passed to React's useReducer
* @return {Array} of [state, dispatch]
*/
const useReducerWithMiddleware = (
middlewares,
reducer,
initOrInitialValue,
init
) => {
/*
* In order to give access to last state to middleware functions, need to
* provide them a value which itself doesn't change to avoid constantly
* re-defining the middlewares. `useRef` provides just the "box" which we can
* mutate for this purpose.
*/
const lastStateRef = useRef();
// `useMemo` will be discussed in next lecture
const refUpdatingReducer = useMemo(
() => (prevState, action) => {
const newState = reducer(prevState, action);
lastStateRef.current = newState;
return newState;
},
[lastStateRef]
);
const [state, dispatch] = useReducer(
refUpdatingReducer,
initOrInitialValue,
init
);
const dispatchWithMiddlewares = useMemo(() => {
const middlewareInit = {
getStateBefore: () => lastStateRef.current,
};
return middlewares
.concat(dispatch)
.reverse()
.reduce((acc, middleware) => {
/*
* Initialize the middleware with means to get state and the next
* middleware/final dispatch.
*/
return middleware(middlewareInit)(acc);
});
}, [middlewares, dispatch, lastStateRef]);
return [state, dispatchWithMiddlewares];
};
export default useReducerWithMiddleware;