【问题标题】:Angular2 state management and the correct usage of @ngrx/storeAngular2状态管理和@ngrx/store的正确使用
【发布时间】:2017-03-02 20:33:13
【问题描述】:

我正在使用 ngrx/store 为我的应用程序编写减速器。

这是我的应用程序状态的布局:

{
    project: {
        name: string
        tasks: Array<Task>
    }
}

with:

interface Task {
    name: string
}

我正在尝试在单独的文件中编写干净的减速器。

这是我目前使用的解决方案:

project.reducer.ts

import {tasksReducer} from './tasks.reducer';

const projectReducer = (state:Project = null, action: Action): Project => {
    switch (action.type) {
        case 'CREATE_PROJECT':
            return {
                name :'New project',
                tasks: []
            };
    };

    state.tasks = tasksReducer( state.tasks, action );

    return state;
}

tasks.reducer.ts

export const tasksReducer = (state:Array<Task> = [], action: Action): Array<Task> => {
    switch (action.type) {
        case 'ADD_TASK':
            return [...state, { name: 'New task' ];
        default:
            return state;
    };
}

使用以下方式提供商店:

StoreModule.provideStore( combineReducers([{
    project: projectReducer
}]) );

如果我想在我的项目中添加其他字段,例如标签字段:

{
    project: {
        name: string,
        tasks: Array<Task>,
        tags: Array<Tags>
    }
}

我可以创建一个单独的tags.reducer.ts,并使用相同的方法创建相应的reducer。

那么这种方法有什么问题呢?

我很确定我在应用程序状态的不变性方面遇到了麻烦。

例子:

  • 我调度 CREATE_PROJECT 操作,我得到一个新状态,一切正常。
  • 然后我发送一个 ADD_TASK 动作。
    • tasksReducer 本身返回一个全新的任务数组,但是主应用程序状态发生了变异......这并不好!

在您看来,解决这个问题的最佳方法是什么?

更一般地说:

随着我的项目对象会随着更多的领域越来越大,我该怎么做:

  • 保持减速器分开
  • 使减速器在“主”减速器的隔离子部分上运行
  • 保持我的状态不可变

我很乐意分享这方面的意见和意见!

【问题讨论】:

    标签: angular store ngrx


    【解决方案1】:

    首先是关于你的 projectReducer 的一些评论。 您在 switch 语句中缺少默认情况,这非常重要。 (见http://blog.kwintenp.com/how-to-write-clean-reducers-and-test-them/#defaultcase)。所以把这个添加到那个减速器中:

    default:
       return state;
    

    其次,你在调用你的 taskReducer 时做了一些奇怪的事情。当调用 projectReducer 时,您所做的是总是调用您的 taskReducer。实际上,当且仅当这是他可以做某事的动作时,您才想调用 taskReducer:

    case 'ADD_TASK':
       // call it here
    

    您还覆盖了当前的状态及其任务属性。正如你所说,你正在改变你的状态,这绝对是要避免的事情。您可以利用 Object.assign 运算符来完成您想要的操作(完全修复):

    const projectReducer = (state:Project = null, action: Action): Project => {
        switch (action.type) {
            case 'CREATE_PROJECT':
                return {
                    name :'New project',
                    tasks: []
                };
            case 'ADD_TASK':
                return Object.assign({}, state, {tasks: tasksReducer( state.tasks, action )};
            default:
                return state;
        };
    }
    

    现在,当您更改任务的某些内容时,您正在创建一个新的 Project 对象,这正是您想要的。

    我建议您阅读我的整篇博文,了解如何以简洁的方式编写 reducer。

    您的一般问题可能会有详细的答案,但我猜这会跳过这一点。下面是一些简短的答案。

    保持减速器分开

    --> 您正在使用 combineReducers 辅助方法,这是保持您的减速器分离的好方法。

    让reducer在'main'reducer的独立子部分上运行

    --> 同上

    保持我的状态不可变

    --> 遵循我博文中的提示,并在需要时使用 Object.assign 和扩展运算符。

    【讨论】:

    • 我同意你的提议,但我对这种方法有一个主要反对意见:关注点分离。 projectReducer 必须知道其他“子减速器”实现的每个动作类型。示例:这里的 projectReducer 必须知道 ADD_TASK 动作。这对我来说听起来有问题:随着项目变得越来越大,这将容易出错:对于添加到子 reducer 的每个新操作,我们必须考虑修改根(或父)reducer 以完成缺失的 case 语句。 我希望 sub reducer 能够自给自足
    猜你喜欢
    • 1970-01-01
    • 2016-05-24
    • 1970-01-01
    • 1970-01-01
    • 2017-01-12
    • 1970-01-01
    • 2018-08-27
    • 1970-01-01
    • 2017-07-19
    相关资源
    最近更新 更多