我想知道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;
}
}
我只是在犹豫,每次我想改变一个条目的时候,要不要复制整个列表。你的解决方案是什么?这里有什么最好的做法?我非常感谢你的建议。
1条答案
按热度按时间uemypmqf1#
在Redux中,状态的每一部分都应该是不可变的,但在dart列表中,默认情况下是可变的。
因此,每次你把一个列表传递给某个东西时,你都会防御性地复制你的列表,以防止列表的意外变异,是吗?- )
如果你的状态只包含不可变的列表/集合/Map,你可以安全地传递它们,而不用在每次你授权访问这个列表时复制它们。
但是我想在这个愚蠢的不可改变的列表上添加一些东西!好吧,你不能!它是不可改变的!
因此,您创建了一个全新的不可变列表,其中包含旧列表的副本和新项目。但是,由于这个新列表是不可变的,因此您不必每次传递它时都复制它!这就是拯救应用性能的原因!复制一次,传递它而不必每次都复制。
Dart本身并没有很好方法来创建或处理不可变列表,但是--就像许多其他常见问题一样--有一个很好的包可以实现这个目的:
我想,这个名字形容得再贴切不过了,到底是确有其事!
每一个列表方法,都会改变原始列表,而返回一个新的不可变列表。
您可以使用
IList<MyType>
来代替List<MyType> myList
。而不是
您使用
而不是
您使用
创建IList很容易:例如,
[2, 5, 6].lock
会建立包含值[2, 5, 6]
的IList。不变性可能会牺牲一些性能,但通常不会,即使这样,也是值得的!不要担心任何意外的变化,只需将你的不变性对象传递给每个需要它的对象:不再有防御性复制!异步代码中的竞争条件问题更少,...
好的,你需要一个
copyWith
方法来处理你的不可变对象,但是如果你使用redux,你将需要这个方法来处理你的状态!是的,有一个很棒的包,用于不可变对象和copyWith方法的生成:
freezed
您只需要在构造函数中定义属性,freezed就可以完成这项工作。
它会创建字段、
copyWith
方法、相等运算符+hashCode
、toString
方法,如果需要,甚至还会创建json序列化。此外,它还为您提供了一种创建和使用联合类型的好方法。对于状态和操作类(或者事件类,如果您使用
bloc
的话)来说很不错。redux +冻结+快速不可变集合=完美匹配
(in我诚实的意见!)
顺便说一句:如果您想在应用程序中的某个地方使用可变列表,而不是复制for循环中的每一项,您可以只编写
如果你需要一个
IList myIList
的可变副本,你可以使用myIList.unlock
。