Sharing state behaves strangely when using NavigationLink #3427
-
Hi, I found that sharing state can behave strangely when using NavigationLink. import ComposableArchitecture
import SwiftUI
@main
struct SampleApp: App {
var body: some Scene {
WindowGroup {
ParentView(
store: .init(
initialState: ParentReducer.State()
) {
ParentReducer()
} withDependencies: {
$0.defaultAppStorage = .init(
suiteName: "some-suite"
)!
}
)
}
}
}
@Reducer
struct ParentReducer {
@ObservableState
struct State: Equatable {
@Shared(.someID) var someID
var path = StackState<Path.State>()
}
enum Action {
case onAppear
case buttonTapped
case path(StackActionOf<Path>)
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onAppear:
state.someID = "some value"
return .none
case .buttonTapped:
state.path.append(
.child(.init())
)
return .none
case .path:
return .none
}
}
.forEach(\.path, action: \.path)
}
@Reducer(state: .equatable)
enum Path {
case child(ChildReducer)
}
}
struct ParentView: View {
@Bindable var store: StoreOf<ParentReducer>
var body: some View {
NavigationStack(
path: $store.scope(
state: \.path,
action: \.path
)
) {
List {
// Shared state is nil when child view is appeared.
NavigationLink(state: ParentReducer.Path.State.child(.init())) {
Text("Go to child view")
}
// Shared state is not nil when child view is appeared.
Button {
store.send(.buttonTapped)
} label: {
Text("Go to child view")
}
}
.onAppear {
store.send(.onAppear)
}
} destination: { store in
switch store.case {
case let .child(store):
ChildView(store: store)
}
}
}
}
@Reducer
struct ChildReducer {
@ObservableState
struct State: Equatable {
@Shared(.someID) var someID
}
enum Action {
case onAppear
}
var body: some ReducerOf<Self> {
Reduce { state, action in
switch action {
case .onAppear:
print(state.someID)
return .none
}
}
}
}
struct ChildView: View {
let store: StoreOf<ChildReducer>
var body: some View {
Color.black
.onAppear {
store.send(.onAppear)
}
}
}
extension PersistenceReaderKey where Self == PersistenceKeyDefault<AppStorageKey<String?>> {
public static var someID: Self {
PersistenceKeyDefault(.appStorage("someID"), nil)
}
} As shown in the code, there is no problem for appending to I've only seen a little bit of the code inside TCA. I would appreciate it if you could tell me if I'm using the sharing state incorrectly. Information:
|
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
@kalupas226 Because you are overriding the In general we think |
Beta Was this translation helpful? Give feedback.
@kalupas226 Because you are overriding the
defaultAppStorage
dependency in your store/reducer, the@Shared
lookup is going to be different there compared to in the view, where you are creating the navigation link, and so whenever you reference shared state, like when creating your child feature's state, this needs to be done in the store/reducer, as well.In general we think
NavigationLink(state:)
is probably not a tool for anything but the most basic use cases, and that you will typically have to move this logic into the reducer eventually.