正如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_CHILD
,DELETE_CHILD
,RENAME_CHILD
,MOVE_CHILD
,有两种情况(通过操作或缩减器中的更改状态):
动作:
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 node
和add node
的所有逻辑都是由UPDATE_CHILD_NODES
中的parentNode
行动完成的。
动作:
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,以便在不同的地方生成状态变量(但我喜欢把它们放在一起。 ..)
对此有何意见?
答案 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