繁体   English   中英

SwiftUI - 使用 ForEach 时,您应该在子视图中使用 `@State var` 还是 `let`

[英]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 varcourse字段。 下面代码中的附加注释。

当导航链接打开时,带有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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM