【问题标题】:A best practice for ngrx/redux action contextngrx/redux 操作上下文的最佳实践
【发布时间】:2017-12-27 14:39:04
【问题描述】:

我试图为这种情况找到最佳实践,但我没有找到。

问题: 我不想重复操作文件,就像在我的示例中一样 home-todos.actions 和 sport-todos-actions,我想使用相同的 to-dos.action 文件。和相同的减速器。

示例: 例如,我编写了一个 todo 应用程序,在此示例中,如果我调度一个类型为 'ADD_TODO_ASYNC' 的动作,它将在 home(effects and reducer) 和 sport(effects and reducer) 中调度,您会看到问题

tod​​os.actions.ts

const ADD_TODO_ASYNC = 'ADD TODO ASYNC';
const ADD_TODO_COMPLETE = 'ADD TODO COMPLETE';
const ADD_TODO_FAILD = 'AD TODO FAILD';

class addTodoComplete {
    type = ADD_TODO_COMPLETE;
}
class addTodoFaild {
    type = ADD_TODO_COMPLETE;
}

export type Actions = addTodoComplete | addTodoFaild;

sport.effects.ts

@Injectable()
export class SportTodosService {

    @Effect() ADD_TODO_ASYNC$ = this.actions$.ofType(TodosActionTypes.ADD_TODO_ASYNC)
    .map(toPayload)
    .swithMap( (todo: Todo) => this.api.addTodo(todo))
    .map((todo: Todo) => TodosActionTypes.addTodoComplete(todo))
    constructor(
        private actions$: Actions,
        private api: api
    ) { }

}

home.effects.ts

export class HomeTodosService {
    @Effect() ADD_TODO_ASYNC$ = this.actions$.ofType(TodosActionTypes.ADD_TODO_ASYNC)
        ...
    constructor(
        ...
    ) { }

}

减速器

function todosReducer(state, action: TodosActionTypes.Actions) {
    switch (action.type) {
        case TodosActionTypes.ADD_TODO_COMPLETE:
            return state;
        default:
            return state;
    }
}

app.module.ts

@NgModule({
    declarations: [
      AppComponent
    ],
    imports: [
      StoreModule.forRoot({
        sport: todosReducer,
        home: todosReducer,
      }),
      EffectsModule.forRoot([
        SportTodosService
        HomeTodosService,
      ])
    ],
    providers: [
        api
    ],
    bootstrap: [AppComponent]
  })
  export class AppModule { }

我试图了解这种情况的最佳做法是什么? 编写带有“HOME_ADD_TODO”和“SPORT_ADD_TODO”等上下文的操作?

还是有官方方式?

如果你知道解决方案,不管解决方案是针对 redux 还是 ngrx

谢谢大家

【问题讨论】:

    标签: javascript angular typescript redux ngrx


    【解决方案1】:

    要理解这个问题,您需要重新考虑您的应用架构。 一般来说,可重复使用的 reducer/actions 是不正确的。

    为什么不正确?在目前看来,编写可重用的 reducer 和 action,更少的样板,而不是“DRY”似乎很棒。在您的应用程序示例中。家庭和运动的“ADD_TO_DO”是相等的。

    但将来会很危险,认为您的老板/客户需要未来的运动 add_to_do。如果您更改可重用减速器中的逻辑。你的应用会坏掉。 (您可以使用 if 语句开始修补可重复使用的 reducer 以使其工作,但如果您的应用程序增长,它将不灵活/可读/维护)。

    是的,在这种情况下,您似乎需要编写 2 个减速器和 2 个动作文件。目前它是平等的,但在未来它将是优势和灵活的。

    祝你好运!

    【讨论】:

      【解决方案2】:

      Here 是 ngrx 的一些模式和实践的链接。

      它旨在按照您描述的方式工作。 this.actions$ 是一个 Observable,所以无论你在哪里使用它都会发出。由于TodosActionTypes.ADD_TODO_ASYNChome.effects.tssport.effects.ts 中的类型相同,因此它会在两个地方发出。

      我不确定您是否可以避免在您的情况下执行单独的操作,但您可以减少样板代码的数量。

      我会尝试这样的:

      tod​​os.actions.ts

      abstract class addTodoComplete{
         constructor(readonly type: string){
            //rest of the behavior
         }
      }
      abstract class addTodoFailed{
         constructor(readonly type: string){
           //rest of the behavior
         }
      }
      

      tod​​os.sport-actions.ts

      const ADD_TODO = "[Sport] Add Todo";
      const ADD_TODO_FAILED = "[Sport] Add Todo Failed";
      class sportsAddTodoComplete extends addTodoComplete{
         constructor(){
            super(ADD_TODO);
            //rest of the behavior
         }
      }
      class sportsAddTodoFailed extends addTodoFailed{
         constructor(){
           super(ADD_TODO_FAILED);
            //rest of the behavior
         }
      }
      

      家庭版也是如此。

      此外,您可能会有单独的SportTodosActionTypesHomeTodosActionTypes

      你不会完全摆脱“复制粘贴”,但它应该在一定程度上有所帮助。

      编辑:

      至于 reducer,确实使用这种方法你必须编写两个 reducer,但它不一定是“复制粘贴”工作

      sport.reducer.ts

      import { todoReducer } from './reducer';
      
      export function sportsTodoReducer(state, action: SportTodoActionTypes.Actions){
         todoReducer(state, action);
      }
      

      home 版本也是如此。

      【讨论】:

      • 我知道他们会同时发射!我举个例子,来说明问题!我不喜欢你的方式,因为在这种情况下,我需要为运动动作编写 todos reducer,为家庭动作编写 todos reducer
      • 我知道他们会同时发射!我举个例子,来说明问题!动作不是问题,如果我们为上下文编写独特的动作,在你的解决方案中我需要 2 个减速器
      • 我已经编辑了我的帖子。如果你想分别处理它们,你将不得不为运动和家庭制定不同的行动。这意味着不同的动作、减速器、效果。只能减少复制粘贴代码的数量。
      • 很好,它会工作,但类型不会工作,再看一下减速器。我需要那种类型才能工作
      • 如果您也扩展该部分,它会。试试TodoActionTypes.Actions = SportsTodoActionTypes.Actions | SportsTodoActionTypes.Actions
      【解决方案3】:

      在这些情况下的解决方案是使用操作命名空间

      动作常量充当动作的唯一标识符。由于应用程序中可能有许多动作对应于存储的不同切片,因此我们可以保护我们的存储免受重复动作逻辑故障的一种方法是使用动作命名空间的概念。看看这个:

      // todos.actions.ts
      export const ADD_TODO = '[Home] Add Todo';
      

      我们只是将命名空间附加到操作常量,理想情况下,它对应于我们正在使用的商店切片的名称 - 通常是您当前正在处理的功能模块的名称。

      如果我们发现自己通过记录操作来调试应用程序,这个命名空间将清楚地表明我们正在排除哪些存储切片和操作上下文,因为我们会看到类似这样的内容(假设我们将视图从“主页”切换到“运动”):

      [Home] Add Todo
      [Home] Add Todo Success
      [Sport] Add Todo
      [Sport] Add Todo Success
      

      查看SOURCE了解更多详情

      【讨论】:

      • 我知道名称间距,但我补充说,如果我的应用中有 10 个待办事项主题,我会复制相同的操作吗?在您的解决方案中,我将如何在一个减速器中处理它?
      • 我想你不明白我的问题!因为按照你的方式我需要写两次 reducer 和 action
      猜你喜欢
      • 2017-08-15
      • 2017-09-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-04-29
      相关资源
      最近更新 更多