-
Notifications
You must be signed in to change notification settings - Fork 18
/
index.js
87 lines (79 loc) · 2.19 KB
/
index.js
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
85
86
87
import { useReducer, useEffect, useRef } from "react";
const NO_UPDATE_SYMBOL = Symbol("NO_UPDATE_SYMBOL");
export const Update = newState => ({ newState });
export const NoUpdate = () => NO_UPDATE_SYMBOL;
export const UpdateWithSideEffect = (newState, newSideEffect) => ({
newState,
newSideEffect
});
export const SideEffect = newSideEffect => ({ newSideEffect });
async function executeSideEffects({ sideEffects, state, dispatch }) {
let cancelFuncs = [];
if (sideEffects) {
while (sideEffects.length) {
const sideEffect = sideEffects.shift();
const cancel = sideEffect(state, dispatch);
if (cancel && typeof cancel === "function") {
cancelFuncs.push(cancel);
}
}
}
return Promise.resolve(cancelFuncs);
}
function finalReducer(reducer) {
return function(state, action) {
if (action === NO_UPDATE_SYMBOL) {
return state;
}
let { newState, newSideEffect } = reducer(state.state, action);
const newSideEffects = Array.isArray(newSideEffect) ? newSideEffect : [newSideEffect];
newSideEffects && newSideEffects.length ? [
...state.sideEffects,
...newSideEffect
]
: state.sideEffects;
return {
state: newState || state.state,
sideEffects: newSideEffects
};
};
}
export default function useCreateReducerWithEffect(
reducer,
initialState,
init
) {
const [{ state, sideEffects }, dispatch] = useReducer(
finalReducer(reducer),
{
state: initialState,
sideEffects: []
},
init
);
let cancelFuncs = useRef([]);
useEffect(() => {
if (sideEffects.length) {
async function asyncEffects() {
async function runSideEffects() {
const cancels = await executeSideEffects({
sideEffects,
state,
dispatch
});
return cancels;
}
const cancels = await runSideEffects();
cancelFuncs.current = cancels;
}
asyncEffects();
if (cancelFuncs.current.length) {
cancelFuncs.current.forEach(func => {
func(state);
cancelFuncs.current = [];
});
}
}
}, [sideEffects]); //eslint-disable-line
return [state, dispatch];
}