@@ -2,46 +2,137 @@ import { EqualityFn } from './types'
22import { assertInInjectionContext , effect , inject , Signal , signal } from '@angular/core'
33import { ReduxProvider } from './provider'
44
5- export interface UseSelectorOptions < Selected = unknown > {
5+ export interface InjectSelectorOptions < Selected = unknown > {
66 equalityFn ?: EqualityFn < Selected >
77}
88
99const refEquality : EqualityFn < any > = ( a , b ) => a === b
1010
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
4085
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+ } )
43101 } )
102+
103+ return selectedState
104+ }
105+
106+ Object . assign ( injectSelector , {
107+ withTypes : ( ) => injectSelector ,
44108 } )
45109
46- return selectedState
110+ return injectSelector as InjectSelector
47111}
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