@@ -138,6 +138,7 @@ public final class Store<State, Action> {
138
138
private var isSending = false
139
139
private let reducer : ( inout State , Action ) -> Effect < Action , Never >
140
140
private var bufferedActions : [ Action ] = [ ]
141
+ fileprivate var scope : AnyScope ?
141
142
#if DEBUG
142
143
private let mainThreadChecksEnabled : Bool
143
144
#endif
@@ -307,29 +308,9 @@ public final class Store<State, Action> {
307
308
action fromChildAction: @escaping ( ChildAction ) -> Action
308
309
) -> Store < ChildState , ChildAction > {
309
310
self . threadCheck ( status: . scope)
310
- var isSending = false
311
- let childStore = Store < ChildState , ChildAction > (
312
- initialState: toChildState ( self . state) ,
313
- reducer: . init { childState, childAction, _ in
314
- isSending = true
315
- defer { isSending = false }
316
- let task = self . send ( fromChildAction ( childAction) )
317
- childState = toChildState ( self . state)
318
- if let task = task {
319
- return . fireAndForget { await task. cancellableValue }
320
- } else {
321
- return . none
322
- }
323
- } ,
324
- environment: ( )
325
- )
326
- childStore. parentDisposable = self . producer
327
- . skip ( first: 1 )
328
- . startWithValues { [ weak childStore] newValue in
329
- guard !isSending else { return }
330
- childStore? . state = toChildState ( newValue)
331
- }
332
- return childStore
311
+
312
+ return ( self . scope ?? Scope ( root: self ) )
313
+ . rescope ( self , state: toChildState, action: fromChildAction)
333
314
}
334
315
335
316
/// Scopes the store to one that exposes child state.
@@ -566,3 +547,69 @@ public final class Store<State, Action> {
566
547
#endif
567
548
}
568
549
}
550
+
551
+ private protocol AnyScope {
552
+ func rescope< State, Action, ChildState, ChildAction> (
553
+ _ store: Store < State , Action > ,
554
+ state toNewChildState: @escaping ( State ) -> ChildState ,
555
+ action fromNewChildAction: @escaping ( ChildAction ) -> Action
556
+ ) -> Store < ChildState , ChildAction >
557
+ }
558
+
559
+ private struct Scope < RootState, RootAction> : AnyScope {
560
+ let root : Store < RootState , RootAction >
561
+ let toChildState : Any
562
+ let fromChildAction : Any
563
+
564
+ init ( root: Store < RootState , RootAction > ) {
565
+ self . init ( root: root, toChildState: { $0 } , fromChildAction: { $0 } )
566
+ }
567
+
568
+ private init < State, Action> (
569
+ root: Store < RootState , RootAction > ,
570
+ toChildState: @escaping ( RootState ) -> State ,
571
+ fromChildAction: @escaping ( Action ) -> RootAction
572
+ ) {
573
+ self . root = root
574
+ self . toChildState = toChildState
575
+ self . fromChildAction = fromChildAction
576
+ }
577
+
578
+ func rescope< State, Action, ChildState, ChildAction> (
579
+ _ store: Store < State , Action > ,
580
+ state toChildState: @escaping ( State ) -> ChildState ,
581
+ action fromChildAction: @escaping ( ChildAction ) -> Action
582
+ ) -> Store < ChildState , ChildAction > {
583
+ let toState = self . toChildState as! ( RootState ) -> State
584
+ let fromAction = self . fromChildAction as! ( Action ) -> RootAction
585
+
586
+ var isSending = false
587
+ let childStore = Store < ChildState , ChildAction > (
588
+ initialState: toChildState ( store. state) ,
589
+ reducer: . init { childState, childAction, _ in
590
+ isSending = true
591
+ defer { isSending = false }
592
+ let task = self . root. send ( fromAction ( fromChildAction ( childAction) ) )
593
+ childState = toChildState ( store. state)
594
+ if let task = task {
595
+ return . fireAndForget { await task. cancellableValue }
596
+ } else {
597
+ return . none
598
+ }
599
+ } ,
600
+ environment: ( )
601
+ )
602
+ childStore. parentDisposable = store. producer
603
+ . skip ( first: 1 )
604
+ . startWithValues { [ weak childStore] newValue in
605
+ guard !isSending else { return }
606
+ childStore? . state = toChildState ( newValue)
607
+ }
608
+ childStore. scope = Scope < RootState , RootAction > (
609
+ root: self . root,
610
+ toChildState: { toChildState ( toState ( $0) ) } ,
611
+ fromChildAction: { fromAction ( fromChildAction ( $0) ) }
612
+ )
613
+ return childStore
614
+ }
615
+ }
0 commit comments