关于 reducer 与 Action 的新手问题。 From redux documentation :
Actions describe the fact that something happened, but don’t specify how the application’s state changes in response.
和
Given the same arguments, reducer should calculate the next state and return it. No surprises. No side effects. No API calls. No mutations. Just a calculation.
因此,如果我们考虑以下场景:
- 用户可以在 map 上放置点并获取这些点之间的路线。
- 当用户第一次点击 map 时,这是他的起点。当他第二次点击时——这就是他的终点。随后的点击会在前一个点和结束位置之间添加点。
- 添加每个点(第一个点除外)后,必须计算新点和前一个点之间的路线。因此,如果我有 S -> A -> F 并添加点 B (S -> A -> B -> F) 必须计算两条路线 A -> B 和 B -> F
因此,在添加任何 3 个以上的点时,我们会产生两个副作用:
- 新点未放置在列表末尾
- 必须计算到达终点的新路线。
如果我将 Point 结构建模如下:
// typescript
interface Point {
coordinates;
routeTo?;
}
我在操作中执行项目位置计算和路线检索是否正确,例如:
// pseudo code
export function addMapPoint(newPoint) {
return (dispatch, getState) => {
const {points} = getState();
const position = getInsertPosition(points, newPoint)
dispatch(addPoint(newPoint, position));
if (points.length >= 2) {
const previousPoint = getPreviousPoint(points, position);
calculateRoute(newPoint, previousPoint).then(route => {
dispatch(updateRoute(newPoint, route))
})
}
}
}
对我来说,这在某种程度上与“但不指定应用程序的状态如何变化”相矛盾 - 因为从操作中我指定了在哪里插入我的新点。
我可以计算reducer中的插入位置,但是如何获取终点的路线数据?
这里正确的做法是什么?
最佳答案
假设我们有 calculateRoute
函数接受两个点,并返回一个解决它们之间路线的 promise 。
首先,让我们创建一个简单的 Action 创建器,以便我们知道我们的点已正确存储:
let addPoint = (point, index) => {
return {
type: 'ADD_POINT',
point: point,
index: index
}
}
然后,让我们在reducer中处理这个 Action :
let reducer = (state = { points: [] }, action) => {
switch (action.type) {
case 'ADD_POINT':
return Object.assign({}, state, {
points: [
...state.points.slide(0, action.index),
action.point,
...state.points.slide(action.index + 1)
]
});
default:
return state;
}
}
现在,在用户添加点后,我们使用 addPoint
创建一个操作并发送它,到目前为止一切顺利,但这是简单的事情。
我追求的结构是在我的 reducer 中也有一个路由列表,所以让我们扩展它以支持它:
let reducer = (state = { points: [], routes: [] }, action) => {
switch (action.type) {
case 'ADD_POINT':
return Object.assign({}, state, {
points: [
...state.points.slide(0, action.index),
action.point,
...state.points.slide(action.index + 1)
]
});
case 'UPDATE_ROUTES':
return Object.assign({}, state, {
routes: action.routes
});
default:
return state;
}
}
Action 创建者将是:
let updateRoutes = (routes) => {
return {
type: 'UPDATE_ROUTES',
routes: routes
}
}
请注意,我们正在覆盖整个路线集合。目前还可以,但在生产系统中您可能需要对其进行一些优化。
现在我们实际上需要编写一些逻辑。我将假设一个方便的假设,即我们有 calculateRoutes
获取点的集合,并返回一个解析相应路线列表的 promise ,每个路线将是一个对象,包含两个点和实际路线。话虽如此,我们的thunk
现在看起来像这样:
addPointAndUpdateRoutes = (point, index) => {
return (dispatch, getState) => {
// First, update the point list
dispatch(addPoint(point, index));
// Now, recalculate routes
calculateRoutes(getState().points)
.then(routes => dispatch(updateRoutes(routes));
};
};
我认为这更好。
现在,当然假设我们有一个神奇的 calculateRoutes
函数并不是一个严肃的假设,尽管以优化的方式实现这个函数并不是一个 super 困难的任务(意味着实际上只向服务器发送我们之前没有计算过的路由等)。 但是这只是逻辑,不是应用程序的状态,因此,只要您保留存储和 reducer 定义的“契约”,您就可以自由地实现随心所欲。
希望这对您有帮助。
关于redux reducer vs Action ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37746358/