-
Notifications
You must be signed in to change notification settings - Fork 0
/
reducer.ts
75 lines (69 loc) · 1.97 KB
/
reducer.ts
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
import { Signal, untracked } from "@preact-signals/unified-signals";
import { IfNever } from "type-fest";
declare class ReducerSignal<T, TAction = never> extends Signal<T> {
constructor(value: T, reducer: Reducer<T, TAction>);
/**
*
* dispatch is automatically binds to signals, so you can destructure it
* @param args
*/
dispatch: (...args: IfNever<TAction, [], [payload: TAction]>) => void;
/** @internal */
_r: Reducer<T, TAction>;
}
export type Reducer<T, TAction> = (state: T, action: TAction) => T;
function reducerSignalDispatcher<T, TAction>(
this: ReducerSignal<T, TAction>,
action: TAction
) {
this.value = untracked(() => this._r(this.peek(), action));
}
function ReducerSignal<T, TAction = never>(
this: ReducerSignal<T, TAction>,
value: T,
reducer: Reducer<T, TAction>
) {
Signal.call(this, value);
this._r = reducer;
// allowing to extract Dispatch
// @ts-expect-error
this.dispatch = reducerSignalDispatcher.bind(this);
}
ReducerSignal.prototype = Object.create(Signal.prototype);
export { ReducerSignal };
/**
*
* @description implementation of Reducer pattern for signals
* @example
* ```tsx
* const reducer = (it: number, action: { type: 'increment' | 'decrement' }) => {
* switch (action.type) {
* case 'increment':
* return it + 1
* case 'decrement':
* return it - 1
* }
* }
*
* const counter = reducerSignal(0, reducer)
*
* effect(() => {
* console.log('counter value', counter.value)
* })
* // prints 1
* counter.dispatch({ type: 'increment' })
*
* // dispatch can be destructured, other parameters not
* const { dispatch } = reducerSignal
* // prints 2
* dispatch({ type: 'increment' })
* ```
*
* @param value initialState
* @param reducer reducer function (do not track reactive dependencies)
* @returns
*/
export const reducerSignal = /*#__NO_SIDE_EFFECTS__*/ <T, TAction = never>(
value: T,
reducer: Reducer<T, TAction>
): ReducerSignal<T, TAction> => new ReducerSignal(value, reducer);