[英]SwiftUI - Should you use `@State var` or `let` in child view when using ForEach
I think I've a gap in understanding what exactly @State
means, especially when it comes to displaying contents from a ForEach
loop.我想我在理解
@State
确切@State
存在差距,尤其是在显示ForEach
循环中的内容时。
My scenario: I've created minimum reproducible example.我的场景:我创建了最小的可重现示例。 Below is a parent view with a
ForEach
loop.下面是一个带有
ForEach
循环的父视图。 Each child view has a NavigationLink
.每个子视图都有一个
NavigationLink
。
// Parent code which passes a Course instance down to the child view - i.e. CourseView
struct ContentView: View {
@StateObject private var viewModel: ViewModel = .init()
var body: some View {
NavigationView {
VStack {
ForEach(viewModel.courses) { course in
NavigationLink(course.name + " by " + course.instructor) {
CourseView(course: course, viewModel: viewModel)
}
}
}
}
}
}
class ViewModel: ObservableObject {
@Published var courses: [Course] = [
Course(name: "CS101", instructor: "John"),
Course(name: "NS404", instructor: "Daisy")
]
}
struct Course: Identifiable {
var id: String = UUID().uuidString
var name: String
var instructor: String
}
Actual Dilemma: I've tried two variations for the CourseView
, one with let
constant and another with a @State var
for the course
field.实际困境:我为
CourseView
尝试了两种变体,一种是let
常量,另一种是带有@State var
的course
字段。 Additional comments in the code below.下面代码中的附加注释。
The one with the let
constant successfully updates the child view when the navigation link is open.当导航链接打开时,带有
let
常量的那一个成功地更新了子视图。 However, the one with @State var
doesn't update the view.但是,带有
@State var
不会更新视图。
struct CourseView: View {
// Case 1: Using let constant (works as expected)
let course: Course
// Case 2: Using @State var (doesn't update the UI)
// @State var course: Course
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
Text("\(course.name) by \(course.instructor)")
Button("Edit Instructor", action: editInstructor)
}
}
// Case 1: It works and UI gets updated
// Case 2: Doesn't work as is.
// I've to directly update the @State var instead of updating the clone -
// which sometimes doesn't update the var in my actual project
// (that I'm trying to reproduce). It definitely works here though.
private func editInstructor() {
let instructor = course.instructor == "Bob" ? "John" : "Bob"
var course = course
course.instructor = instructor
save(course)
}
// Simulating a database save, akin to something like GRDB
// Here, I'm just updating the array to see if ForEach picks up the changes
private func save(_ courseToSave: Course) {
guard let index = viewModel.courses.firstIndex(where: { $0.id == course.id }) else {
return
}
viewModel.courses[index] = courseToSave
}
}
What I'm looking for is the best practice for a scenario where looping through an array of models is required and the model is updated in DB from within the child view.我正在寻找的是需要循环遍历模型数组并且模型从子视图内在 DB 中更新的场景的最佳实践。
Here is a right way for you, do not forget that we do not need put logic in View!这是适合您的正确方法,不要忘记我们不需要在 View 中放置逻辑! the view should be dummy as possible!
视图应该是虚拟的!
struct ContentView: View {
@StateObject private var viewModel: ViewModel = ViewModel.shared
var body: some View {
NavigationView {
VStack {
ForEach(viewModel.courses) { course in
NavigationLink(course.name + " by " + course.instructor, destination: CourseView(course: course, viewModel: viewModel))
}
}
}
}
}
struct CourseView: View {
let course: Course
@ObservedObject var viewModel: ViewModel
var body: some View {
VStack {
Text("\(course.name) by \(course.instructor)")
Button("Update Instructor", action: { viewModel.update(course) })
}
}
}
class ViewModel: ObservableObject {
static let shared: ViewModel = ViewModel()
@Published var courses: [Course] = [
Course(name: "CS101", instructor: "John"),
Course(name: "NS404", instructor: "Daisy")
]
func update(_ course: Course) {
guard let index = courses.firstIndex(where: { $0.id == course.id }) else {
return
}
courses[index] = Course(name: course.name, instructor: (course.instructor == "Bob") ? "John" : "Bob")
}
}
struct Course: Identifiable {
let id: String = UUID().uuidString
var name: String
var instructor: String
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.