@@ -2,7 +2,8 @@ package org.reduxkotlin
22
33import kotlin.jvm.JvmField
44
5- private typealias EqualityCheckFn = (a: Any ,b: Any )-> Boolean
5+ private typealias EqualityCheckFn = (a: Any , b: Any ) -> Boolean
6+
67/* *
78 * A rewrite for kotlin of https://github.com/reactjs/reselect library for redux (https://github.com/reactjs/redux)
89 * see also "Computing Derived Data" in redux documentation http://redux.js.org/docs/recipes/ComputingDerivedData.html
@@ -18,13 +19,6 @@ val byRefEqualityCheck: EqualityCheckFn = { a: Any, b: Any -> a === b }
1819 */
1920val byValEqualityCheck: EqualityCheckFn = { a: Any , b: Any -> a == b }
2021
21- /* *
22- * a class for keeping a non null object reference even when actual reference is null
23- * Needed because selectors do not work for nullable fields: if you have a field in the state that is
24- * nullable T? the define instead as Opt<T> if you want selectors to work
25- */
26- class Opt <T >(@JvmField val it : T ? )
27-
2822interface Memoizer <T > {
2923 fun memoize (state : Any , vararg inputs : SelectorInput <Any , Any >): T
3024}
@@ -114,24 +108,6 @@ interface Selector<S, O> : SelectorInput<S, O> {
114108 getIfChangedIn(state)?.let (blockfn)
115109 }
116110
117- /* *
118- * same as regular [onChangeIn] but don't activate selector unless [condition] is true
119- * Note that the selector is not run at all if [condition] is false. Therefore
120- * the selector will be triggered by changes that happened and where ignored, as soon as
121- * [condition] become true
122- */
123- fun onChangeIn (state : S , condition : Boolean ,blockfn : (O ) -> Unit ) {
124- if (condition) getIfChangedIn(state)?.let (blockfn)
125- }
126- }
127-
128- /* *
129- * same as [Selector.onChangeIn], but as extension function of state:
130- * it checks if the specified selector value is changed for the input state and if so, call [blockfn]
131- * with the updated selector value
132- */
133- fun <S ,O > S.whenChangeOf (selector : Selector <S , O >, blockfn : (O ) -> Unit ) {
134- selector.getIfChangedIn(this )?.let (blockfn)
135111}
136112
137113/* *
@@ -164,156 +140,12 @@ abstract class AbstractSelector<S, O> : Selector<S, O> {
164140
165141}
166142
167- // use @JvmField annotation for avoiding generation useless getter methods
168- class SelectorForP5 <S : Any , I0 : Any , I1 : Any , I2 : Any , I3 : Any , I4 : Any >(@JvmField val si0 : SelectorInput <S , I0 >,
169- @JvmField val si1 : SelectorInput <S , I1 >,
170- @JvmField val si2 : SelectorInput <S , I2 >,
171- @JvmField val si3 : SelectorInput <S , I3 >,
172- @JvmField val si4 : SelectorInput <S , I4 >
173- ) {
174- fun <O > compute (equalityCheckForResult : EqualityCheckFn = byRefEqualityCheck, computeFun : (I0 , I1 , I2 , I3 , I4 ) -> O ) = object : AbstractSelector <S , O >() {
175- override val equalityCheck: EqualityCheckFn
176- get() = equalityCheckForResult
177- override val computeAndCount = fun (i : Array <out Any >): O {
178- ++ _recomputations
179- @Suppress(" UNCHECKED_CAST" )
180- return computeFun(i[0 ] as I0 , i[1 ] as I1 , i[2 ] as I2 , i[3 ] as I3 , i[4 ] as I4 )
181- }
182-
183- override operator fun invoke (state : S ): O {
184- @Suppress(" UNCHECKED_CAST" )
185- return memoizer.memoize(
186- state,
187- si0 as SelectorInput <Any , Any >,
188- si1 as SelectorInput <Any , Any >,
189- si2 as SelectorInput <Any , Any >,
190- si3 as SelectorInput <Any , Any >,
191- si4 as SelectorInput <Any , Any >
192- )
193- }
194- }
195- }
196-
197- // use @JvmField annotation for avoiding generation useless getter methods
198- class SelectorForP4 <S : Any , I0 : Any , I1 : Any , I2 : Any , I3 : Any >(@JvmField val si0 : SelectorInput <S , I0 >,
199- @JvmField val si1 : SelectorInput <S , I1 >,
200- @JvmField val si2 : SelectorInput <S , I2 >,
201- @JvmField val si3 : SelectorInput <S , I3 >
202- ) {
203- fun <I4 : Any > withField (fn : S .() -> I4 ) = SelectorForP5 <S , I0 , I1 , I2 , I3 , I4 >(si0, si1, si2, si3, InputField (fn, byRefEqualityCheck))
204- fun <I4 : Any > withFieldByValue (fn : S .() -> I4 ) = SelectorForP5 <S , I0 , I1 , I2 , I3 , I4 >(si0, si1, si2, si3, InputField (fn, byValEqualityCheck))
205- fun <I4 : Any > withSelector (si : SelectorInput <S , I4 >) = SelectorForP5 <S , I0 , I1 , I2 , I3 , I4 >(si0, si1, si2, si3, si)
206- fun <O > compute (equalityCheckForResult : EqualityCheckFn = byRefEqualityCheck, computeFun : (I0 , I1 , I2 , I3 ) -> O ) = object : AbstractSelector <S , O >() {
207- override val equalityCheck: EqualityCheckFn
208- get() = equalityCheckForResult
209- override val computeAndCount = fun (i : Array <out Any >): O {
210- ++ _recomputations
211- @Suppress(" UNCHECKED_CAST" )
212- return computeFun(i[0 ] as I0 , i[1 ] as I1 , i[2 ] as I2 , i[3 ] as I3 )
213- }
214-
215- override operator fun invoke (state : S ): O {
216- @Suppress(" UNCHECKED_CAST" )
217- return memoizer.memoize(
218- state,
219- si0 as SelectorInput <Any , Any >,
220- si1 as SelectorInput <Any , Any >,
221- si2 as SelectorInput <Any , Any >,
222- si3 as SelectorInput <Any , Any >
223- )
224- }
225- }
226- }
227-
228- // use @JvmField annotation for avoiding generation useless getter methods
229- class SelectorForP3 <S : Any , I0 : Any , I1 : Any , I2 : Any >(@JvmField val si0 : SelectorInput <S , I0 >,
230- @JvmField val si1 : SelectorInput <S , I1 >,
231- @JvmField val si2 : SelectorInput <S , I2 >
232- ) {
233- fun <I3 : Any > withField (fn : S .() -> I3 ) = SelectorForP4 (si0, si1, si2, InputField (fn, byRefEqualityCheck))
234- fun <I3 : Any > withFieldByValue (fn : S .() -> I3 ) = SelectorForP4 (si0, si1, si2, InputField (fn, byValEqualityCheck))
235- fun <I3 : Any > withSelector (si : SelectorInput <S , I3 >) = SelectorForP4 (si0, si1, si2, si)
236- fun <O > compute (equalityCheckForResult : EqualityCheckFn = byRefEqualityCheck, computeFun : (I0 , I1 , I2 ) -> O ) = object : AbstractSelector <S , O >() {
237- override val equalityCheck: EqualityCheckFn
238- get() = equalityCheckForResult
239- override val computeAndCount = fun (i : Array <out Any >): O {
240- ++ _recomputations
241- @Suppress(" UNCHECKED_CAST" )
242- return computeFun(i[0 ] as I0 , i[1 ] as I1 , i[2 ] as I2 )
243- }
244-
245- override operator fun invoke (state : S ): O {
246- @Suppress(" UNCHECKED_CAST" )
247- return memoizer.memoize(
248- state,
249- si0 as SelectorInput <Any , Any >,
250- si1 as SelectorInput <Any , Any >,
251- si2 as SelectorInput <Any , Any >
252- )
253- }
254- }
255- }
256-
257- // use @JvmField annotation for avoiding generation useless getter methods
258- class SelectorForP2 <S : Any , I0 : Any , I1 : Any >(@JvmField val si0 : SelectorInput <S , I0 >,
259- @JvmField val si1 : SelectorInput <S , I1 >
260- ) {
261- fun <I2 : Any > withField (fn : S .() -> I2 ) = SelectorForP3 <S , I0 , I1 , I2 >(si0, si1, InputField (fn, byRefEqualityCheck))
262- fun <I2 : Any > withFieldByValue (fn : S .() -> I2 ) = SelectorForP3 <S , I0 , I1 , I2 >(si0, si1, InputField (fn, byValEqualityCheck))
263- fun <I2 : Any > withSelector (si : SelectorInput <S , I2 >) = SelectorForP3 <S , I0 , I1 , I2 >(si0, si1, si)
264- fun <O > compute (equalityCheckForResult : EqualityCheckFn = byRefEqualityCheck, computeFun : (I0 , I1 ) -> O ) = object : AbstractSelector <S , O >() {
265- override val equalityCheck: EqualityCheckFn
266- get() = equalityCheckForResult
267- override val computeAndCount = fun (i : Array <out Any >): O {
268- ++ _recomputations
269- @Suppress(" UNCHECKED_CAST" )
270- return computeFun(i[0 ] as I0 , i[1 ] as I1 )
271- }
272-
273- override operator fun invoke (state : S ): O {
274- @Suppress(" UNCHECKED_CAST" )
275- return memoizer.memoize(
276- state,
277- si0 as SelectorInput <Any , Any >,
278- si1 as SelectorInput <Any , Any >
279- )
280- }
281- }
282- }
283-
284- // use @JvmField annotation for avoiding generation useless getter methods
285- class SelectorForP1 <S : Any , I0 : Any >(@JvmField val si0 : SelectorInput <S , I0 >) {
286- fun <I1 : Any > withField (fn : S .() -> I1 ) = SelectorForP2 <S , I0 , I1 >(si0, InputField (fn, byRefEqualityCheck))
287- fun <I1 : Any > withFieldByValue (fn : S .() -> I1 ) = SelectorForP2 <S , I0 , I1 >(si0, InputField (fn, byValEqualityCheck))
288- fun <I1 : Any > withSelector (si : SelectorInput <S , I1 >) = SelectorForP2 <S , I0 , I1 >(si0, si)
289- fun <O > compute (equalityCheckForResult : EqualityCheckFn = byRefEqualityCheck, computeFun : (I0 ) -> O ) = object : AbstractSelector <S , O >() {
290- override val equalityCheck: EqualityCheckFn
291- get() = equalityCheckForResult
292- override val computeAndCount = fun (i : Array <out Any >): O {
293- ++ _recomputations
294- @Suppress(" UNCHECKED_CAST" )
295- return computeFun(i[0 ] as I0 )
296- }
297-
298- override operator fun invoke (state : S ): O {
299- @Suppress(" UNCHECKED_CAST" )
300- return memoizer.memoize(
301- state,
302- si0 as SelectorInput <Any , Any >
303- )
304- }
305- }
306- }
307143
308144/* *
309145 * wrapper class for Selector factory methods , that basically is used only to capture
310146 * type information for the state parameter
311147 */
312148class SelectorBuilder <S : Any > {
313- fun <I0 : Any > withField (fn : S .() -> I0 ) = SelectorForP1 <S , I0 >(InputField (fn, byRefEqualityCheck))
314- fun <I0 : Any > withFieldByValue (fn : S .() -> I0 ) = SelectorForP1 <S , I0 >(InputField (fn, byValEqualityCheck))
315- fun <I0 : Any > withSelector (si : SelectorInput <S , I0 >) = SelectorForP1 <S , I0 >(si)
316-
317149 /* *
318150 * special single input selector that should be used when you just want to retrieve a single field:
319151 * Warning: Don't use this with primitive type fields, use [withSingleFieldByValue] instead!!!
@@ -363,5 +195,38 @@ class SelectorBuilder<S:Any> {
363195 }
364196}
365197
198+ /* *
199+ * Helper function that creates a DSL for subscribing to changes in specific state fields and actions to take.
200+ * Inside the lambda there is access to the current state through the var `state`
201+ *
202+ * ex:
203+ * val sel = selectorSubscriberFn {
204+ * select({it.foo}, { actionWhenFooChanges() }
205+ *
206+ * withAnyChange {
207+ * //called whenever any change happens to state
208+ * view.setMessage(state.barMsg) //state is current state
209+ * }
210+ * }
211+ */
212+ fun <State : Any >Store<State>.selectors (selectorSubscriberBuilderInit : SelectorSubscriberBuilder <State >.() -> Unit ): StoreSubscriber {
213+ val subscriberBuilder: SelectorSubscriberBuilder <State > = SelectorSubscriberBuilder (this )
214+ subscriberBuilder.selectorSubscriberBuilderInit()
215+ val sub = {
216+ subscriberBuilder.selectorList.forEach { entry ->
217+ @Suppress(" UNCHECKED_CAST" )
218+ (entry.key as Selector <State , * >).onChangeIn(getState()) { entry.value(getState()) }
219+ }
220+ subscriberBuilder.withAnyChangeFun?.invoke()
221+ Unit
222+ }
223+ // call subscriber immediately when subscribing
224+ sub()
225+ return this .subscribe(sub)
226+ }
366227
367-
228+ fun <State : Any >Store<State>.select (selector : (State ) -> Any , action : (Any ) -> Unit ): StoreSubscriber {
229+ return subscribe(this .selectors {
230+ select(selector, action)
231+ })
232+ }
0 commit comments