【问题标题】:The proper place to change state in react redux在 react redux 中更改状态的正确位置
【发布时间】:2016-09-30 06:34:24
【问题描述】:

正如the doc 所说:

你不应该在 reducer 中做的事情:

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

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

例如,

这样的默认减速器:

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,有两个 sulotions(通过更改动作或减速器的状态):

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 减速器:

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

所有变量都从动作传入 parentNode,parentNode 只是将更改分配给 state 不会做任何其他事情。

remove nodeadd node 的所有逻辑都是通过动作完成的,只有parentNode 中的UPDATE_CHILD_NODES

2。 Action 只是将数据发送给 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 减速器:

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 来改变状态是否很好?当我需要通过Date.now() 设置updateTime 时,我必须从动作中生成它并传递给reducer,以便在不同的地方生成状态变量(但我想把它们放在一起......)

对此有何看法?

【问题讨论】:

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


    【解决方案1】:

    来自这个 redux 讨论 here:

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

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

    Action-creators 可以通过使用像 redux-thunk 这样的中间件来实现异步。由于您的应用程序通常需要对您的存储进行异步更新 - 一些“业务逻辑”最终会出现在您的操作中。

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

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

    【讨论】:

    【解决方案2】:

    对于小型应用程序,我通常将我的逻辑放在动作创建器中。对于更复杂的情况,您可能需要考虑其他选项。以下是不同方法优缺点的总结: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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2016-08-31
      • 2020-01-28
      • 2017-08-12
      • 1970-01-01
      • 2022-01-21
      相关资源
      最近更新 更多