简体   繁体   English

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

[英]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 varcourse字段。 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.

相关问题 如何在 SwiftUI 中使用 Foreach 视图? - How to use Foreach on view in SwiftUI? 如何将项目添加到在父视图中的 ObservableObject 中初始化/包装的全局列表变量,并让它在子视图中的 SwiftUI 中的 ForEach 中反映? - How to add an item to a global list var initted/wrapped in ObservableObject in parent view and have it reflect in ForEach in SwiftUI in a child view? SwiftUI - 如何在更新父视图的 state 时保留子视图的 state? - SwiftUI - How to preserve child view's state when updating parent view's state? SwiftUI - 使用 var 或 let 从两个文本字段中添加值 - SwiftUI - Add values from two textfields using var or let SwiftUI View 在嵌入 UIView 时不会更新其状态(使用 UIHostingController) - SwiftUI View not updating its state when embedded in UIView (using UIHostingController) 无法更新/修改 SwiftUI 视图的 @state var - Unable to update/modify SwiftUI View's @state var 我应该使用var还是让对象稍后发生突变? - Should I use var or let for an object later mutated? 如果让 SwiftUI 进入 View - SwiftUI if let inside View 如何在 SwiftUI 中使用类型为 func 的 @State var 占位符 - How to use a @State var placeholder of type func in SwiftUI SwiftUI 使用 StateObject、Form 元素和 ForEach 的视图在尝试在表单内使用 NavigationLink 时会中断绑定 - SwiftUI View with StateObject, a Form element and a ForEach breaks bindings when trying to use NavigationLink Inside the form
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM