🐥
TCA で View を分割する方法
SwiftUI で View を作成していると、View のコードが大きくなってきたりするので View を分割したいと思うことは少なくないと思います。
しかし、TCA では View において Store を基本的に保持する構成になっており、View に WithViewStore
なども絡んでくるため、普通に分割することができません。
この記事では、あまり良くないパターンも含めた2パターンについて説明しようと思います。
- ViewStore を function 経由で引き渡す方法(TCA ではこれが分割方法として良さそう)
- Store を引き渡す分割方法(あまり良くない)
ViewStore を function 経由で引き渡す方法
Point-Free さんが公開されている公式の Example でもこの方法で実装していたため、おそらく TCA 的にはこれが一番良さそうな方法かなと思っています。
例えば以下のような ParentView があった時、
struct ParentView: View {
let store: Store<ExampleState, ExampleAction>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
Text("Example Text")
// この HStack を分割したい
HStack {
Text("この HStack が分割されます")
}
}
}
}
}
以下のように function の引数として viewStore
を指定することによって、View を分割することができます。
struct ParentView: View {
let store: Store<ExampleState, ExampleAction>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
Text("Example Text")
childView(viewStore: viewStore)
}
}
}
func childView(
viewStore: ViewStore<ExampleState, ExampleAction>
) -> some View {
HStack {
Text("この HStack が分割されます")
}
}
}
Store を引き渡す分割方法
ParentView
については引き続き同様のものとします。
Store を引き渡す分割方法は SwiftUI で普通に View を分割しようとした時と同じような方法になります。具体的には以下のように分割することができます。
struct ParentView: View {
let store: Store<ExmapleState, ExampleAction>
var body: some View {
WithViewStore(store) { viewStore in
VStack {
Text("Example Text")
ChildView(store: store)
}
}
}
}
struct ChildView: View {
let store: Store<ExampleState, ExampleAction>
var body: some View {
WithViewStore(store) { viewStore in
HStack {
Text("この HStack が分割されます")
}
}
}
}
簡単ではありますが、store をそのまま子 View に渡しているだけです。
自分は最近までこの方法で実装してしまっていましたが、子 View に毎回 store
を渡すのは冗長であることに加え、WithViewStore
が必要になり、子 View に不要なネストを強いてしまうことも微妙です。
おわりに
単純な話でしたが、意外とわからなくなったりするので記事として残しておきました。
ちょうど今 TCA で適当なアプリを作っているところなので、得られた知見などあればどんどん記事にしていこうと思います。
Discussion
「ViewStore を function 経由で引き渡す方法」のchildViewの引数の型は
Store<ExampleState, ExampleAction>
ではなく、ViewStore<ExampleState, ExampleAction>
の誤りかもです。ありがとうございます!!
修正させていただきました🙏