为什么在redux中需要'Actions'作为数据?

Sør*_*ois 9 javascript typescript redux

Redux文档说我应该创建动作和动作创建者,如下所示:

function addTodo(filter) {
  return { 
    type: SET_VISIBILITY_FILTER, 
    filter
  }
}
Run Code Online (Sandbox Code Playgroud)

然后写减速器,像这样:

function todoApp(state = initialState, action) {
  switch (action.type) {
   case SET_VISIBILITY_FILTER:
     return Object.assign({}, state, {
       visibilityFilter: action.filter
     });
  } 
}
Run Code Online (Sandbox Code Playgroud)

然后我使用dispatch调用该操作:

store.dispatch(addTodo("Ask question on stackoverflow"));
Run Code Online (Sandbox Code Playgroud)

似乎行动和减速器之间存在一对一的对应关系; 该操作的唯一目的是选择减速器并为该减速器提供输入数据.

为什么我们不跳过中间人并用减速器和具有减速器功能的动作创建器识别动作?然后dispatch将采用单个参数,类型的reducer/action State => State:

// Action/reducer. (Parametrised state transformer, really.)
const addTodo = text => state => {
  return Object.assign({}, state, {
       visibilityFilter: action.filter
  });
}

// Dispatch takes as a argument the action/reducer
store.dispatch(addTodo("Ask question on stackoverflow"));
Run Code Online (Sandbox Code Playgroud)

您将失去序列化操作的能力,但除此之外,您似乎摆脱了样板操作创建者并更清楚地表达了操作和减速器之间的联系.如果您使用的是Typescript,那么您还可以对操作中的数据进行类型检查,否则很难表达.

那么我错过了采取数据行动的原因是什么?

mar*_*son 6

操作和化简器之间不存在一对一的映射。根据 Dan Abramov 在https://github.com/Pitzcarraldo/reduxible/issues/8的评论:

\n
\n

它强化了关于 Redux 的一个非常常见的误解:即操作创建者和缩减者是一对一映射。

\n

这仅在简单的示例中成立,但在实际应用中却受到极大限制。接触这种模式的初学者将减速器和动作创建器结合在一起,但没有意识到它们应该是多对多和解耦的。

\n

许多减速器可以处理一个动作。一个减速器可以处理许多动作。将它们放在一起会抵消 Flux 和 Redux 应用程序扩展的许多好处。这会导致代码膨胀和不必要的耦合。您失去了对来自不同位置的相同操作做出反应的灵活性,并且您的操作创建者开始像 \xe2\x80\x9csetters\xe2\x80\x9d 一样行事,耦合到特定的状态形状,从而也将组件耦合到它。

\n
\n

至于操作和“类型”参数,其他答案是正确的。Redux 就是这样设计的,旨在为调试目的提供序列化的好处。

\n


Dmi*_*nko 5

Redux中的主要行动目的是减少状态. 将在动作数组上调用Reduce方法(这就是为什么它称为 reducer).例:

import reducer from './reducer';

const actions = [
    {type: 'INIT'},
    {type: 'SOME_ACTION', params: {...}},
    {type: 'RECEIVE_DATA', data: [...]},
    {type: 'SOME_ANOTHER_ACTION', params: {...}},
    {type: 'RECEIVE_DATA', data: [...]},
    ...
];

const finalState = actions.reduce(reducer, undefined);
Run Code Online (Sandbox Code Playgroud)

动作创建者是一个可以创建动作的功能.动作创建者不必仅创建一个动作.

实际上,如果你的reducer能够接收函数而不是对象 - 你的动作将是函数,它将完成主要目的,但你可以放弃Redux功能的一些好处.

在这种情况下,reducer将实现如下:

function reducer(state, action) {
    return action(state);
}
Run Code Online (Sandbox Code Playgroud)

您可以以{type: 'ACTION_NAME'}格式创建操作的原因:

  1. Redux DevTools期望这种格式.
  2. 您需要存储操作序列.
  3. Reducer对worker进行状态转换.
  4. Redux生态系统中的每个人都使用这种格式.这是一种惯例.
  5. 热重新加载功能(不会重新加载您存储的函数).
  6. 您需要在服务器上按原样发送操作.
  7. 调试的好处 - 查看具有操作名称的操作堆栈.
  8. 写减速器的单元测试:assert.equal(finalState, expectedState).
  9. 更多声明性代码 - 动作名称和参数是关于"做什么"而不是"如何做"(但addTodo('Ask question')也是声明性的).

注意动作创建者和状态变化之间的耦合

只需比较两种表示法:

第一:

function someActionCreator() {
    return {
        type: "ADD_TODO",
        text: "Ask question on stackoverflow"
    }; // returns object
}
Run Code Online (Sandbox Code Playgroud)

第二:

function someActionCreator() {
    return addTodo("Ask question on stackoverflow"); // returns function
}
Run Code Online (Sandbox Code Playgroud)

"在这两种情况下,我们都看到代码是声明性的,动作创建者与状态变化分离.你仍然可以重用addTodo或调度两个addTodo或使用中间件或调度compose(addTodo('One'), addTodo('Two')).主要区别在于我们创建了对象和函数并放置在代码中的状态变化.