@@ -2,46 +2,137 @@ import { EqualityFn } from './types'
2
2
import { assertInInjectionContext , effect , inject , Signal , signal } from '@angular/core'
3
3
import { ReduxProvider } from './provider'
4
4
5
- export interface UseSelectorOptions < Selected = unknown > {
5
+ export interface InjectSelectorOptions < Selected = unknown > {
6
6
equalityFn ?: EqualityFn < Selected >
7
7
}
8
8
9
9
const refEquality : EqualityFn < any > = ( a , b ) => a === b
10
10
11
- // TODO: Add support for `withTypes`
12
- export function injectSelector < TState = unknown , Selected = unknown > (
13
- selector : ( state : TState ) => Selected ,
14
- equalityFnOrOptions : EqualityFn < Selected > | UseSelectorOptions < Selected > = { } ,
15
- ) : Signal < Selected > {
16
- assertInInjectionContext ( injectSelector )
17
- const reduxContext = inject ( ReduxProvider ) ;
18
-
19
- const { equalityFn = refEquality } =
20
- typeof equalityFnOrOptions === 'function'
21
- ? { equalityFn : equalityFnOrOptions }
22
- : equalityFnOrOptions
23
-
24
- const {
25
- store,
26
- subscription
27
- } = reduxContext
28
-
29
- const selectedState = signal ( selector ( store . getState ( ) ) )
30
-
31
- effect ( ( onCleanup ) => {
32
- const unsubscribe = subscription . addNestedSub ( ( ) => {
33
- const data = selector ( store . getState ( ) ) ;
34
- if ( equalityFn ( selectedState ( ) , data ) ) {
35
- return
36
- }
37
-
38
- selectedState . set ( data ) ;
39
- } )
11
+ /**
12
+ * Represents a custom injection that allows you to extract data from the
13
+ * Redux store state, using a selector function. The selector function
14
+ * takes the current state as an argument and returns a part of the state
15
+ * or some derived data. The injection also supports an optional equality
16
+ * function or options object to customize its behavior.
17
+ *
18
+ * @template StateType - The specific type of state this injection operates on.
19
+ *
20
+ * @public
21
+ */
22
+ export interface InjectSelector < StateType = unknown > {
23
+ /**
24
+ * A function that takes a selector function as its first argument.
25
+ * The selector function is responsible for selecting a part of
26
+ * the Redux store's state or computing derived data.
27
+ *
28
+ * @param selector - A function that receives the current state and returns a part of the state or some derived data.
29
+ * @param equalityFnOrOptions - An optional equality function or options object for customizing the behavior of the selector.
30
+ * @returns The selected part of the state or derived data.
31
+ *
32
+ * @template TState - The specific type of state this injection operates on.
33
+ * @template Selected - The type of the value that the selector function will return.
34
+ */
35
+ < TState extends StateType = StateType , Selected = unknown > (
36
+ selector : ( state : TState ) => Selected ,
37
+ equalityFnOrOptions ?: EqualityFn < Selected > | InjectSelectorOptions < Selected > ,
38
+ ) : Signal < Selected >
39
+
40
+ /**
41
+ * Creates a "pre-typed" version of {@linkcode injectSelector injectSelector}
42
+ * where the `state` type is predefined.
43
+ *
44
+ * This allows you to set the `state` type once, eliminating the need to
45
+ * specify it with every {@linkcode injectSelector injectSelector} call.
46
+ *
47
+ * @returns A pre-typed `injectSelector` with the state type already defined.
48
+ *
49
+ * @example
50
+ * ```ts
51
+ * export const injectAppSelector = injectSelector.withTypes<RootState>()
52
+ * ```
53
+ *
54
+ * @template OverrideStateType - The specific type of state this injection operates on.
55
+ */
56
+ withTypes : <
57
+ OverrideStateType extends StateType ,
58
+ > ( ) => InjectSelector < OverrideStateType >
59
+ }
60
+
61
+ /**
62
+ * Injection factory, which creates a `injectSelector` injection bound to a given context.
63
+ *
64
+ * @returns {Function } A `injectSelector` injection bound to the specified context.
65
+ */
66
+ export function createSelectorInjection ( ) : InjectSelector {
67
+ const injectSelector =
68
+ < TState , Selected > (
69
+ selector : ( state : TState ) => Selected ,
70
+ equalityFnOrOptions : EqualityFn < Selected > | InjectSelectorOptions < Selected > = { } ,
71
+ ) : Signal < Selected > =>
72
+ {
73
+ assertInInjectionContext ( injectSelector )
74
+ const reduxContext = inject ( ReduxProvider ) ;
75
+
76
+ const { equalityFn = refEquality } =
77
+ typeof equalityFnOrOptions === 'function'
78
+ ? { equalityFn : equalityFnOrOptions }
79
+ : equalityFnOrOptions
80
+
81
+ const {
82
+ store,
83
+ subscription
84
+ } = reduxContext
40
85
41
- onCleanup ( ( ) => {
42
- unsubscribe ( )
86
+ const selectedState = signal ( selector ( store . getState ( ) ) )
87
+
88
+ effect ( ( onCleanup ) => {
89
+ const unsubscribe = subscription . addNestedSub ( ( ) => {
90
+ const data = selector ( store . getState ( ) ) ;
91
+ if ( equalityFn ( selectedState ( ) , data ) ) {
92
+ return
93
+ }
94
+
95
+ selectedState . set ( data ) ;
96
+ } )
97
+
98
+ onCleanup ( ( ) => {
99
+ unsubscribe ( )
100
+ } )
43
101
} )
102
+
103
+ return selectedState
104
+ }
105
+
106
+ Object . assign ( injectSelector , {
107
+ withTypes : ( ) => injectSelector ,
44
108
} )
45
109
46
- return selectedState
110
+ return injectSelector as InjectSelector
47
111
}
112
+
113
+ /**
114
+ * A injection to access the redux store's state. This injection takes a selector function
115
+ * as an argument. The selector is called with the store state.
116
+ *
117
+ * This injection takes an optional equality comparison function as the second parameter
118
+ * that allows you to customize the way the selected state is compared to determine
119
+ * whether the component needs to be re-rendered.
120
+ *
121
+ * @param {Function } selector the selector function
122
+ * @param {Function= } equalityFn the function that will be used to determine equality
123
+ *
124
+ * @returns {any } the selected state
125
+ *
126
+ * @example
127
+ *
128
+ * import { injectSelector } from 'angular-redux'
129
+ *
130
+ * @Component ({
131
+ * selector: 'counter-component',
132
+ * template: `<div>{{counter}}</div>`
133
+ * })
134
+ * export class CounterComponent {
135
+ * counter = injectSelector(state => state.counter)
136
+ * }
137
+ */
138
+ export const injectSelector = /* #__PURE__*/ createSelectorInjection ( )
0 commit comments