The child of IfLetStore doesn't update together with the store. #3244
Replies: 2 comments 4 replies
-
Hi @IvanMukticcc, can you please provide a project that demonstrates the behavior? The code you provided has formatting problems. Also it is not clear from your video what exactly is not working correctly. Can you please clearly state the steps to reproduce the problem, and describe precisely what the problem is. Since it's not clear that this is a problem with the library I am going to convert it to a discussion. Please feel free to continue the conversation over there. |
Beta Was this translation helpful? Give feedback.
-
Hi, Unfortunately, I cannot provide the original project as it is for a client. However, I have recreated the issue in the newest version of TCA (1.11.2), so the code should now work if you copy and paste it into an empty Swift file. Code: import ComposableArchitecture
import SwiftUI
@Reducer
struct MainReducer {
@ObservableState
struct State: Equatable {
var firstChildState: FirstChildReducer.State?
}
enum Action {
case firstChildAction(FirstChildReducer.Action)
case selectFirstChildState(FirstChildReducer.State?)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case let .selectFirstChildState(newState):
state.firstChildState = newState
return .none
case .firstChildAction(.secondChildAction(.playButtonTapped)):
return .send(.selectFirstChildState(nil))
case .firstChildAction:
return .none
}
}
.ifLet(\.firstChildState, action: \.firstChildAction) {
FirstChildReducer()
}
}
}
struct MainView: View {
@Perception.Bindable
var store: StoreOf<MainReducer>
var body: some View {
WithPerceptionTracking {
HStack {
List {
ForEach(1 ..< 5, id: \.self) { num in
Button {
store.send(.selectFirstChildState(.init(number: num)))
} label: {
Text("Num: \(num)")
}
}
}
if store.firstChildState != nil {
if let firstChildStore = store.scope(state: \.firstChildState, action: \.firstChildAction) {
FirstChildView(store: firstChildStore)
.frame(width: UIScreen.main.bounds.width * 0.66)
}
} else {
Text("Select State")
.frame(width: UIScreen.main.bounds.width * 0.66)
}
}
}
}
}
struct FirstChildView: View {
@Perception.Bindable
var store: StoreOf<FirstChildReducer>
var body: some View {
WithPerceptionTracking {
VStack {
SecondChildView(
store: store
.scope(state: \.secondChildState, action: \.secondChildAction)
)
Spacer()
Text("Selected store number: \(store.number)")
.font(.system(size: 30, weight: .bold))
Spacer()
}
}
}
}
@Reducer
struct FirstChildReducer {
@ObservableState
struct State: Equatable, Identifiable {
var id: UUID = .init()
var number: Int
var secondChildState = SecondChildReducer.State()
}
enum Action {
case secondChildAction(SecondChildReducer.Action)
}
var body: some ReducerOf<Self> {
Scope(state: \.secondChildState, action: \.secondChildAction) {
SecondChildReducer()
}
Reduce { _, action in
switch action {
case .secondChildAction:
return .none
}
}
}
}
struct SecondChildView: View {
@Perception.Bindable
var store: StoreOf<SecondChildReducer>
var body: some View {
WithPerceptionTracking {
ZStack {
Rectangle()
.frame(height: UIScreen.main.bounds.height * 0.33)
Button {
store.send(.playButtonTapped)
} label: {
VStack(spacing: 40) {
Text(store.angle == 0 ? "Child of if let store animating..." : "Nothing happens")
.foregroundStyle(.white)
.font(.title)
Image(systemName: "play")
.resizable()
.frame(width: 36, height: 40)
.foregroundStyle(Color.white)
.rotationEffect(.degrees(store.angle))
.animation(.easeInOut(duration: 1), value: store.angle)
}
}
Spacer()
}
.onAppear {
store.send(.angleChanged(to: 0))
}
}
}
}
@Reducer
struct SecondChildReducer {
@ObservableState
struct State: Equatable {
var angle = 90.0
}
enum Action: BindableAction {
case binding(BindingAction<State>)
case angleChanged(to: Double)
case playButtonTapped
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .playButtonTapped:
return .none
case let .angleChanged(newAngle):
state.angle = newAngle
return .none
case .binding(_):
return .none
}
}
}
}
#Preview {
MainView(store: Store(initialState: MainReducer.State(), reducer: {}))
}
Problem: Expected behaviour: Simulator.Screen.Recording.-.iPad.Pro.12.9-inch.6th.generation.16GB.-.2024-07-18.at.11.32.01.mp4 |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
Description
I I have an iPad Split View with a list of items that trigger changes in an IfLetStore, and the state changes as expected. However, the child view inside the IfLetStore does not update accordingly.
Checklist
main
branch of this package.Expected behavior
When selecting a new store, both the store and its children should update.
Actual behavior
Simulator.Screen.Recording.-.iPad.Pro.12.9-inch.6th.generation.16GB.-.2024-07-16.at.10.43.03.mp4
Steps to reproduce
I made simple app to reproduce the bug, here is the code:
import ComposableArchitecture
import SwiftUI
struct MainReducer: Reducer {
struct State: Equatable {
var firstChildState: FirstChildReducer.State?
}
}
struct MainView: View {
var store: StoreOf
}
struct FirstChildView: View {
var store: StoreOf
}
struct FirstChildReducer: Reducer {
struct State: Equatable, Identifiable {
var id: UUID = .init()
var number: Int
}
struct SecondChildView: View {
var store: StoreOf
}
struct SecondChildReducer: Reducer {
struct State: Equatable {
var angle = 90.0
}
}
#Preview {
MainView(store: Store(initialState: MainReducer.State(), reducer: {}))
}
The Composable Architecture version information
from: 1.5.0
Destination operating system
iOS 16.0
Xcode version information
15.2
Swift Compiler version information
Beta Was this translation helpful? Give feedback.
All reactions