redux 在带有嵌套AppState的Flutter中,复位器的最佳实践是什么

vsikbqxv  于 2022-11-12  发布在  Flutter
关注(0)|答案(1)|浏览(141)

我想知道AppState reducer的最佳实践,如果AppState包含列表或其他复杂对象。
举个例子:
设定:比方说,我正在写一个体重跟踪应用,每个体重条目都有一个日期和注解。我想显示用户体重条目的各种信息,所以我决定把它们放到应用状态中。我决定把它们从用户数据中分离出来,放在一个单独的列表中,使应用状态尽可能扁平化,并把不同的方面分开(用户数据、设置、重量条目)。
编码:
应用程序状态:

class AppState{
  //username, birthdate, etc.
  List<Entry> entries;
}

条目:

class Entry{
  String userId;
  double weight;
  DateTime date;
  String comment;
}

2修改/添加条目的操作:

class UpdateCommentOnEntry{
  int index;
  String comment;
}

class AddEntry{
  Entry entry;
}

任务:在AppState Reducer中处理此列表的最佳方式是什么?
关注点:移动的设备上的性能和内存管理。基本上我想尽可能少地复制。
可能的解决方案:
-1)据我所知,此解决方案是错误的/违反了Redux标准,因为它改变了当前状态:

AppState reducer(AppState state, dynamic action) {
  if(action is UpdateCommentOnEntry){
    state.entries[action.index].comment=action.comment;
  } else if(action is AddEntry){
    state.entries.add(action.entry);
  }
  return state;
}

1)此解决方案将复制整个列表

AppState reducer(AppState state, dynamic action) {
  if(action is UpdateCommentOnEntry){
    List<Entry> newList;
    for(int i = 0; i<state.entries.length; i++){
      if(i!=action.index){  
        newList.add(state.entries[i]);
      } else{
        newList.add(state.entries[i].copyWith(comment: action.comment));
      }
    } 
    return state.copyWith(entries = newList);
  } else if(action is AddEntry){
    List<Entry> newList = List<Entry>.from(state.entries);
    newList.entries.add(action.entry);
    return state.copyWith(entries: newList);
  } else {
    return state;
  }
}

2)这个解决方案没有复制列表,但是复制了AppState,但是它仍然引用了同一个列表。而且这个列表确实发生了变化,所以这也违反了Redux标准吗?

AppState reducer(AppState state, dynamic action) {
  if(action is UpdateCommentOnEntry){
    state.entries[i] = state.entries[i].copyWith(comment: action.comment);
    return state.copyWith(entries: state.entries);
  } else if(action is AddEntry){
    List<Entry> newList = List<Entry>.from(state.entries);
    newList.entries.add(action.entry);
    return state.copyWith(entries: newList);
  } else {
    return state;
  }
}

我只是在犹豫,每次我想改变一个条目的时候,要不要复制整个列表。你的解决方案是什么?这里有什么最好的做法?我非常感谢你的建议。

uemypmqf

uemypmqf1#

在Redux中,状态的每一部分都应该是不可变的,但在dart列表中,默认情况下是可变的。
因此,每次你把一个列表传递给某个东西时,你都会防御性地复制你的列表,以防止列表的意外变异,是吗?- )
如果你的状态只包含不可变的列表/集合/Map,你可以安全地传递它们,而不用在每次你授权访问这个列表时复制它们。
但是我想在这个愚蠢的不可改变的列表上添加一些东西!好吧,你不能!它是不可改变的!
因此,您创建了一个全新的不可变列表,其中包含旧列表的副本和新项目。但是,由于这个新列表是不可变的,因此您不必每次传递它时都复制它!这就是拯救应用性能的原因!复制一次,传递它而不必每次都复制。
Dart本身并没有很好方法来创建或处理不可变列表,但是--就像许多其他常见问题一样--有一个很好的包可以实现这个目的:

fast_immutable_collections

我想,这个名字形容得再贴切不过了,到底是确有其事!
每一个列表方法,都会改变原始列表,而返回一个新的不可变列表
您可以使用IList<MyType>来代替List<MyType> myList
而不是

state.entries[action.index].comment = action.comment;

您使用

state.copyWith(entries: state.entries.replace(action.index, action.entry)

而不是

state.entries.add(action.entry);

您使用

state.copyWith(entries: state.entries.add(action.entry)

创建IList很容易:例如,[2, 5, 6].lock会建立包含值[2, 5, 6]的IList。
不变性可能会牺牲一些性能,但通常不会,即使这样,也是值得的!不要担心任何意外的变化,只需将你的不变性对象传递给每个需要它的对象:不再有防御性复制!异步代码中的竞争条件问题更少,...
好的,你需要一个copyWith方法来处理你的不可变对象,但是如果你使用redux,你将需要这个方法来处理你的状态!
是的,有一个很棒的包,用于不可变对象和copyWith方法的生成:freezed
您只需要在构造函数中定义属性,freezed就可以完成这项工作。
它会创建字段、copyWith方法、相等运算符+hashCodetoString方法,如果需要,甚至还会创建json序列化。
此外,它还为您提供了一种创建和使用联合类型的好方法。对于状态和操作类(或者事件类,如果您使用bloc的话)来说很不错。
redux +冻结+快速不可变集合=完美匹配
(in我诚实的意见!)
顺便说一句:如果您想在应用程序中的某个地方使用可变列表,而不是复制for循环中的每一项,您可以只编写

myListCopy = [...myList];

如果你需要一个IList myIList的可变副本,你可以使用myIList.unlock

相关问题