我试图理解为什么Redux是按原样设计的。例如,假设我有一家商店,里面有待办事项清单。
如果商店实际上是这样的对象:
{1: todo1,
2: todo2,
3: todo3, ...}*
然后将其包装在一个类中,该类允许我们执行以下操作:
todoStore.getAll()
todoStore.add(todo)
todoStore.get(id);
todoStore.get([1,2,3,...]);
todoStore.filter((todo)=> todo.id == 1);
todoStore.del(1);
todoStore.update(2, {title: 'new title'}:Partial<Todo>);
....
因此,在这种情况下,我们只拥有一个Todo
模型和一个TodoStore
,该模型具有一个API,可以让我们查询/过滤,删除,更新和添加项目。
为什么Redux需要Actions和Reducer?
答案之一表明:
相反,Redux使用一种模式,当给定状态和动作时,它将始终产生相同的新状态。
因此,由于这种模式,我们似乎需要动作和减速器,但对我而言,它们看起来像是内部实现的细节。 TodoStore
为什么不能仅在内部实现这些功能?
例如,如果我们在缓存中有1个todo实例,而我们又添加了一个,则现在有了2个。这似乎很容易实现……但是我一定会遗漏一些东西……
我当时正在考虑实施类似的
@TodoStore
class Todo {
}
注释/装饰器将生成商店,然后客户将通过以下方式获得商店:
todoStore:TodoStore = StoreCache.get(Todo);
todos:Observable<Todo[]> = todoStore.getAll();
...
等等看起来可能就这么简单...所以只是想知道Redux提供的内容可能会丢失...换句话说,为什么redux决定它需要操作和reducer而不是像Store<Type>
这样的简单界面?
看待它的另一种方式是,Reducers and Action是否添加了Store<Type>
接口无法通过实现方式/语言约束添加的内容?
操作是方法名称和实体名称的组合。因此,例如,如果我们有Store<Todo>
(一种对待办事项类型进行操作的商店类型),并且说一种更新方法(例如update(id:String, todo:Todo)
),那么我们有效地将Action的名称命名为待办事项更新。如果第二个参数为复数,则update(id:String, todos:Todo[])
为 TODO UPDATES ...
如果要进行更新,则必须找到要更新的实例并对其进行更新,并且通常使用ID来执行。更新完成后,如果我们愿意的话,我们可以在不可变的状态树中跟踪它们,例如,可以将整个更改包装在命令对象实例中,以便我们撤消/重播它。我相信Eclipe EMF框架API为此提供了一个很好的模型,并为生成的模型提供了Elipse撤消/重做功能。
答案 0 :(得分:3)
这个问题似乎有点广泛或基于观点,但我会试一试。
Redux存储是不可变的,因此不能更改存储。相反,Redux使用一种模式,当给定状态和动作时,它将始终产生相同的新状态。这些动作是准确的,告诉商店要执行什么动作,然后减速器执行该更改并返回新状态。
如果您来自可变的面向对象的背景,这可能会觉得很奇怪,但是它使您可以浏览状态更改,追溯历史并重播动作等。这是一个强大的模式。
答案 1 :(得分:1)
对于评论来说,这太长了,可能是一个答案,但这更多是关于Redux存在的概念,而不是其实现的具体细节。
考虑看似简单的赋值语句:
var foo = {bar: 1};
foo.bar = 3;
简单吧?除非...如果我需要先前的foo.bar
值怎么办?如果我需要存储对状态转换本身的引用怎么办,例如重新播放吗?语句不是JavaScript中的一流实体:
var transition = foo.bar = 3;
不是很有意义。您可以将其包装为lambda表达式:
var transition = () => { foo.bar = 3 };
但是,这无法捕获过渡语义,只是将foo.bar
的状态设置为3。如果前一个状态对下一个状态很重要,该怎么办?您如何分享foo
?将其填充到全局中,并希望没人对您进行变异吗?但是,如果foo
周围的语义很清晰,则状态会发生变化,否则是不可变的,那么...
有些有用的属性不在此范围内
还有其他人,但是您明白了。现在,您可能并不需要所有这些,也可能不值得失去灵活性(以及Redux would agree的作者Dan Abramov)。但是,通过在系统中提供状态转换为一流的状态,可以获得很多好处。