Redux-like state container in SwiftUI. Container Views.
Another subject from my previous posts
which plays very nice in conjunction with a?Redux-like?state?container, and this is?Container Views.?Container Views?help us to keep our SwiftUI views simple and responsible for only one job.
The main idea is dividing your views into two types:?Container Views?and?Rendering Views. The Rendering View is responsible for drawing the content, and that’s all. So basically it should not store the state or handle any lifecycle event. It usually renders the data which you pass via the init method.
Container View, on another hand, is responsible for handling data-flow and lifecycle events by providing the functions/closures to a Rendering View.
Let’s take a look at a simple example.
import SwiftUI
struct SearchContainerView: View {
@EnvironmentObject var store: ReposStore
@State private var query: String = "Swift"
var body: some View {
SearchView(query: $query, repos: store.repos, onCommit: fetch)
.onAppear(perform: fetch)
}
private func fetch() {
store.fetch(matching: query)
}
}
struct SearchView: View {
@Binding var query: String
let repos: [Repo]
let onCommit: () -> Void
var body: some View {
List {
TextField("Type something", text: $query, onCommit: onCommit)
ReposView(repos: repos)
}
}
}
struct ReposView: View {
let repos: [Repo]
var body: some View {
ForEach(repos) { repo in
HStack(alignment: .top) {
VStack(alignment: .leading) {
Text(repo.name)
.font(.headline)
Text(repo.description ?? "")
.font(.subheadline)
}
}
}
}
}
You can see how we build a connection between?Container and Rendering views.?Container View?provides the data to?Rendering Views. By doing this, we can easily reuse our?ReposView?anywhere across the app.?ReposView?doesn’t have any dependency on some state or datastore and gets all the needed data via the init method.
Have you explored alternatives to all scopes being publicly available in all views that use the Store?i.e:?store.state.search.result
Selectors?and?middleware?are two possible solutions.
Store holds the entire state of the application, and each view can access the Store and subscribe to changes in the state. However, in large-scale applications, it's not always desirable to expose all scopes of the state to all views.
One solution to this is to use selectors. Selectors are functions that take the entire state as input and return a specific subset of the state that a particular view needs. By using selectors, views only have access to the parts of the state they need, and not the entire state.
Another approach is to use middleware. Middleware intercepts actions before they reach the Store and can modify them or dispatch additional actions based on the existing state. This approach can be used to transform the state before it reaches the views, thereby limiting the scope of the state visible to the views.
While all scopes of the state are publicly available in all views that use the Store, there are ways to limit the scope of the state visible to each view -?selectors?and?middleware.
领英推荐
Contacts
I have a clear focus on time-to-market and don't prioritize technical debt. And I took part in the Pre-Sale/RFX activity as a System Architect, assessment efforts for Mobile (iOS-Swift, Android-Kotlin), Frontend (React-TypeScript) and Backend (NodeJS-.NET-PHP-Kafka-SQL-NoSQL). And I also formed the work of Pre-Sale as a CTO from Opportunity to Proposal via knowledge transfer to Successful Delivery.
?? Email:?[email protected]
?? LinkedIn:?https://www.dhirubhai.net/in/sergeyleschev/
?? LeetCode:?https://leetcode.com/sergeyleschev/
?? Twitter:?https://twitter.com/sergeyleschev
?? Github:?https://github.com/sergeyleschev
?? Website:?https://sergeyleschev.github.io
?? DEV Community: https://dev.to/sergeyleschev
?? Reddit: https://reddit.com/user/sergeyleschev
?? Quora: https://quora.com/sergey-leschev
?? Medium: https://medium.com/@sergeyleschev