[英]SwiftUI - Should you use `@State var` or `let` in child view when using ForEach
我想我在理解@State
确切@State
存在差距,尤其是在显示ForEach
循环中的内容时。
我的场景:我创建了最小的可重现示例。 下面是一个带有ForEach
循环的父视图。 每个子视图都有一个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
}
实际困境:我为CourseView
尝试了两种变体,一种是let
常量,另一种是带有@State var
的course
字段。 下面代码中的附加注释。
当导航链接打开时,带有let
常量的那一个成功地更新了子视图。 但是,带有@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
}
}
我正在寻找的是需要循环遍历模型数组并且模型从子视图内在 DB 中更新的场景的最佳实践。
这是适合您的正确方法,不要忘记我们不需要在 View 中放置逻辑! 视图应该是虚拟的!
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.