SwiftUI中使用ForEach时子视图应选@State var还是let接收模型参数
核心原理说明
@State是SwiftUI为视图提供的私有本地状态存储,特性如下:
- 初始化时会把传入的初始值拷贝一份,存储在视图生命周期内独立的私有存储区,默认情况下不会跟随父视图传入的新参数更新本地存储的拷贝,这就是你用@State声明course时UI不更新的原因:当viewModel的courses数组更新触发父视图刷新时,子视图里的@State变量仍然保留着首次打开时的旧Course实例,不会同步父视图新传入的值。
- 用let声明的course是子视图的输入参数,每次父视图重绘时都会被重新赋值为最新值,所以只要viewModel的@Published属性变化触发刷新,子视图就能拿到最新的Course实例,UI会自动同步更新。
循环遍历模型数组+子视图更新场景的最佳实践
- 优先用
let接收外部传入的模型参数
仅做展示、不需要子视图本地修改的模型参数,直接用let声明即可,不需要套任何属性包装器,既不会出现状态同步问题,性能也最优。 - 子视图需要修改模型时,优先使用
@Binding双向绑定
父视图的ForEach改用绑定遍历:
子视图将course声明为ForEach($viewModel.courses) { $course in NavigationLink(course.name + " by " + course.instructor) { CourseView(course: $course, viewModel: viewModel) } }@Binding var course: Course,此时子视图内对course的修改会直接同步到父层的viewModel.courses数组,不需要手动查找索引替换,双向同步自动生效。 - 确实需要子视图持有临时本地状态的场景
如果子视图有编辑中暂不提交到数据源的临时修改需求,确实要用@State存储本地副本的,在数据源更新后主动同步本地@State的值即可:比如示例中save方法执行完成后,主动给self.course = courseToSave,就能触发本地UI刷新。如果需要跟随父视图传入的新值自动更新@State,也可以在创建子视图时添加.id(course.id)修饰符,强制当course.id变化时重建子视图、重新初始化@State的值,但该方案会额外触发视图重建,性能低于前两种方案,非必要不推荐。 - 统一数据源原则
所有需要持久化的模型修改最终都统一更新到全局数据源(比如示例中的viewModel.courses数组、你实际项目中的数据库),不要把子视图本地的@State副本作为持久化的唯一数据来源,避免出现多份数据不同步的问题。
内容的提问来源于stack exchange,提问作者Siddharth Kamaria
相关产品推荐
相关产品推荐

