1 | interface IHookzListener {
|
2 | oldState: any;
|
3 | run?: (state: any) => void;
|
4 | }
|
5 |
|
6 | interface IHookzStore {
|
7 | state: any;
|
8 | actions?: any;
|
9 | listeners: IHookzListener[];
|
10 | setState?: (newState: any, afterUpdateCallback: any) => void;
|
11 | }
|
12 |
|
13 | function setState(store: IHookzStore, newState: any, afterUpdateCallback: any) {
|
14 | store.state = { ...store.state, ...newState };
|
15 | store.listeners.forEach((listener: IHookzListener) => {
|
16 | if (listener.run) {
|
17 | listener.run(store.state);
|
18 | }
|
19 | });
|
20 | if (afterUpdateCallback) {
|
21 | afterUpdateCallback();
|
22 | }
|
23 | }
|
24 | function useCustom(
|
25 | store: IHookzStore,
|
26 | React: any,
|
27 | mapState: any,
|
28 | mapActions: any,
|
29 | ) {
|
30 | const [, originalHook] = React.useState(Object.create(null));
|
31 | const state = mapState ? mapState(store.state) : store.state;
|
32 | const actions = React.useMemo(
|
33 | () => (mapActions ? mapActions(store.actions) : store.actions),
|
34 | [mapActions, store.actions],
|
35 | );
|
36 |
|
37 | React.useEffect(() => {
|
38 | const newListener: IHookzListener = { oldState: {} };
|
39 | newListener.run = mapState
|
40 | ? (newState: any) => {
|
41 | const mappedState = mapState(newState);
|
42 | if (mappedState !== newListener.oldState) {
|
43 | newListener.oldState = mappedState;
|
44 | originalHook(mappedState);
|
45 | }
|
46 | }
|
47 | : originalHook;
|
48 | store.listeners.push(newListener);
|
49 | if (newListener.run) {
|
50 | newListener.run(store.state);
|
51 | }
|
52 | return () => {
|
53 | store.listeners = store.listeners.filter(
|
54 | (listener) => (listener) !== newListener,
|
55 | );
|
56 | };
|
57 | }, []);
|
58 | return [state, actions];
|
59 | }
|
60 |
|
61 | function associateActions({ store, actions }: { store: any; actions: any }) {
|
62 | const associatedActions = {};
|
63 | Object.keys(actions).forEach((key) => {
|
64 | if (typeof actions[key] === "function") {
|
65 |
|
66 | associatedActions[key] = actions[key].bind(null, store);
|
67 | }
|
68 | if (typeof actions[key] === "object") {
|
69 |
|
70 | associatedActions[key] = associateActions({
|
71 | actions: actions[key],
|
72 | store,
|
73 | });
|
74 | }
|
75 | });
|
76 | return associatedActions;
|
77 | }
|
78 |
|
79 | const useHookz = (
|
80 | React: any,
|
81 | initialState: any,
|
82 | actions: any,
|
83 | initializer?: any,
|
84 | ) => {
|
85 | const store: IHookzStore = { state: initialState, listeners: [] };
|
86 | store.setState = setState.bind(null, store);
|
87 | store.actions = associateActions({ store, actions });
|
88 | if (initializer) {
|
89 | initializer(store);
|
90 | }
|
91 | return useCustom.bind(null, store, React);
|
92 | };
|
93 |
|
94 | export default useHookz;
|