Redux为动作和 reducer 减少样板代码

5
我将尝试找出这种情况的最佳实践。
2个上下文:SPORT任务和HOME任务。
因此需要2个操作文件:
export const SPORT_ADD_TODO = `[SPORT] ADD TODO`
export const HOME_ADD_TODO = `[HOME] ADD TODO`

还有2个reducer文件

homeReducer(state, action) {
  switch(action.type) {
     case HOME_ADD_TODO:
        return Object.assing({}, state, {
           todos: action.payload
        })
     default:
        return state;
  }
}

sportReducer(state, action) {
      ....
}

有没有官方解决此问题的方法? 我不想重复自己。 reducer具有相同的功能。


当你说它们具有“相同的功能”时,我很难理解你的意思。由于有两个独立的reducer,它们实际上将“todos”添加到状态树的两个不同部分中。一个在状态的“home”分支中,另一个在“sport”分支中。所以它们实际上是在做同样的事情,还是因为执行了“类似”的操作,方法体只是相似的? - Mark C.
是的,他们对其他分支也在做同样的事情,但我不想每次有新的分支时都复制粘贴。我试图理解这个问题的解决方案,而不是复制粘贴操作和reducers。 - Liam
每个 reducer 只能访问自己的状态。 - Mark C.
3个回答

3

你需要重新考虑你的应用程序架构。通常可重用的reducer/actions是不正确的。

为什么是不正确的呢?从当前的视角来看,编写可重用的reducer和actions似乎非常棒,可以减少样板文件,而且不违背"不要重复自己(DRY)"原则。在你的应用程序示例中,'ADD_TO_DO'在home和sport中相等。

但未来会很危险,想象一下你的老板/客户需要在sports中添加add_to_do功能。如果你改变了可重用的reducer逻辑,你的应用程序将会崩溃(你可以开始使用if语句来修补你的可重用的reducer,使其正常工作,但如果你的应用程序增长,它将不灵活/易读/易于维护)。

所以,在这种情况下似乎需要写2个reducers和2个action文件。现在看起来两者都一样,但在未来这将是有优势和灵活性的。


1

一般来说,在软件开发中,如果我们想要创建多个类似的对象实例,我们使用工厂来进行对象的创建。在Redux上下文中,reducer只是一个纯函数,它是JavaScript中的一种对象类型。因此,这里也适用于工厂模式。

创建一个createTodoReducer工厂:

function createTodoReducer(initialState, {addType}) {
  return function(state = initialState, action) {
    switch(action.type) {
      case addType:
        return {...state, todos: action.payload}
    }
  }
}

现在使用工厂来实例化 sportTodosReducerhomeReducer:
const homeReducer = createTodosReducer({todos: []}, {addType: 'HOME_ADD_TODO'});
const sportsReducer = createTodoReducer({todos: []}, {addType: 'SPORTS_ADD_TODO'})

您可以添加任何类型以修改减速器状态,例如addType,并使用工厂共享相同的逻辑。


这很好,但问题是我需要为任何reducer重新编写所有操作!而且对于TypeScript来说不太友好。 - Liam
由于reducer的逻辑相似,意味着数据类型也相似。因此,您也可以为类似的action creator创建工厂。 - Farzad Yousefzadeh

0

刚刚发现了Autodux

在一个对象中定义了操作、reducer、初始状态和选择器。 如果你只提供了一个初始状态,它会根据状态形状自动生成其余部分。

太棒了!

// From this
const actionTypes = {
  INCREMENT: 'INCREMENT',
  DECREMENT: 'DECREMENT'
}

const actions = {
  increment: () => ({type: actionTypes.INCREMENT}),
  decrement: () => ({type: actionTypes.DECREMENT})
}

const counter = (state = 0, action) => {
  switch (action.type) {
    case actionTypes.INCREMENT:
      return state + 1
    case actionTypes.DECREMENT:
      return state - 1
    default:
      return state
  }
}

// To this
const counter = autodux({
  slice: 'counter',
  initial: 0,
  actions: {
    increment: state => state + 1,
    decrement: state => state - 1
  }
});


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接