反应还原中改变状态的适当位置

时间:2016-09-30 06:34:24

标签: javascript reactjs ecmascript-6 redux react-redux

正如the doc所说:

  

你应该在减速机内做的事情:

     
      
  • 改变其论点;
  •   
  • 执行API调用和路由转换等副作用;
  •   
  • 调用非纯函数,例如Date.now()或Math.random()。
  •   

如果我遵循这个原则,那么关于代码组织有一些问题(我的应用程序是文件管理器)。

例如,

这样的默认reducer:

export default function (state = initialState, action) {
  const { path } = action
  if (typeof path === 'undefined') {
    return state
  }

  const ret = {
    ...state,
    [path]: parentNode(state[path], action)
  };

  switch (action.type) {
    case OPEN_NODE:
    case GO_PATH:
      ret['currentPath'] = path
      break
    default:
      break
  }

  return ret
}

state[path]中的数据结构:

{
    'open': false,
    'path': '/tmp/some_folder',
    'childNodes' : [ {'path':'/some/path', 'mode': '0755', 'isfolder': true}, ....],
    'updateTime': Date.now()
}

现在我需要多个操作,例如ADD_CHILDDELETE_CHILDRENAME_CHILDMOVE_CHILD,有两种情况(通过操作或缩减器中的更改状态):

1。操作中的所有功能代码:

动作:

export function updateChildNodes(path, nodes) {
  return {
    type: UPDATE_CHILD_NODES,
    path: path,
    loading: false,
    loaded: true,
    childNodes: nodes,
  };
}

export function addChild(path, node) {
  return (dispatch, getState) => {
    const state = getState().tree[path]
    var childNodes  = state.childNodes ? state.childNodes :[]
    childNodes.push(node)
    return dispatch(updateChildNodes(path, childNodes))
  }
}

export function deleteChild(parent_path, child_node) {
  return (dispatch, getState) => {
    const state = getState().tree[parent_path]
    var childNodes = state && state.childNodes ? state.childNodes : []
    for (var i=0; i <=childNodes.length; i++){
      if (childNodes[i].path == child_node.path){
        childNodes.splice(i, 1)
        return dispatch(updateChildNodes(parent_path, childNodes))
      }
    }
  }
}

export function deleteNode(node) {
  return (dispatch, getState) => {
    // ajax call
    return api.deleteChild(node.path, () => {
      dispatch(deleteChild(node.parent, node))
    })
  }
}

.....

parentNode reducer:

function parentNode(state, action) {
  switch (action.type) {
    case UPDATE_CHILD_NODES:
      return {
        ...state,
        childNodes: action.childNodes
      }
    default:
      return state;
  }
}

所有变量都从actionNode传递给parentNode,parentNode只是将更改分配给state并不执行任何其他操作。

remove nodeadd node的所有逻辑都是由UPDATE_CHILD_NODES中的parentNode行动完成的。

2。动作只是将数据发送到reducer,让reducer处理

动作:

export function updateChildNodes(path, nodes) {
  return {
    type: UPDATE_CHILD_NODES,
    path: path,
    loading: false,
    loaded: true,
    childNodes: nodes,
  };
}

export function addChild(path, node) {
  return {
    type: ADD_CHILD,
    path: path,
    node: node,
  };
}

export function deleteChild(path, node) {
  return {
    type: DELETE_CHILD,
    path: path,
    node: node,
  };
}

export function deleteNode(node) {
  return (dispatch, getState) => {
    // ajax call
    return api.deleteChild(node.path, () => {
      dispatch(deleteChild(node.parent, node))
    })
  }
}

.....

parentNode reducer:

function parentNode(state, action) {
  switch (action.type) {
    case DELETE_CHILD:
      let childNodes = state.childNodes.slice() // have to clone obj

      for (var i=0; i <=childNodes.length; i++){
        if (childNodes[i].path == action.node.path){
          childNodes.splice(i, 1)
        }
      }

      return {
        ...state,
        childNodes: childNodes
      };
    case ADD_CHILD:
      let childNodes = state.childNodes.slice() // have to clone obj 
      childNodes.push(node)
      return {
        ...state,
        childNodes: childNodes
      };
    case UPDATE_CHILD_NODES:
      return {
        ...state,
        childNodes: action.childNodes
      }
    default:
      return state;
  }
}

在我的选项中,解决方案2更具可读性和漂亮性。 但通过改变一个克隆的obj改变状态是否好?当我需要通过updateTime设置Date.now()时,我必须从动作生成它并传递给reducer,以便在不同的地方生成状态变量(但我喜欢把它们放在一起。 ..)

对此有何意见?

2 个答案:

答案 0 :(得分:2)

来自此redux讨论here

最佳做法是将大多数逻辑放在动作创建器中,并使减速器尽可能简单(更接近您的选项1) 原因如下:

  

业务逻辑属于行动创造者。减速器应该是愚蠢而简单的。在许多个别情况下,这并不重要 - 但是一致性很好,因此最好始终如一地这样做。有几个原因:

     

动作创建者可以通过使用像redux-thunk这样的中间件来异步。由于您的应用程序通常需要对您的商店进行异步更新 - 某些&#34;业务逻辑&#34;将最终成为你的行动。

     

动作创建者(更准确地说是他们返回的thunk)可以使用共享选择器,因为他们可以访问完整状态。减速器不能,因为它们只能访问它们的节点。

     

使用redux-thunk,单个动作创建者可以调度多个动作 - 这使得复杂的状态更新更简单,并鼓励更好的代码重用。

答案 1 :(得分:0)

对于小型应用程序,我通常将我的逻辑放在动作创建者中。对于更复杂的情况,您可能需要考虑其他选项。以下是不同方法的优缺点摘要:https://medium.com/@jeffbski/where-do-i-put-my-business-logic-in-a-react-redux-application-9253ef91ce1#.k8zh31ng5

另外,看看Redux中间件。

中间件在调度操作和到达reducer 之间提供第三方扩展点

这是Dan Abramov(Redux的作者)提供的答案:Why do we need middleware for async flow in Redux?

以下是官方Redux文档:http://redux.js.org/docs/advanced/Middleware.html