redux减速器与动作
关于减速机与行动的新手问题: 动作描述发生了某事的事实,但没有具体说明 应用程序的状态如何随响应而变化 及 给定相同的参数,reducer应该计算下一个状态和 归还它。没有意外。没有副作用。没有API调用。没有突变。 只是计算一下redux减速器与动作,redux,redux-thunk,Redux,Redux Thunk,关于减速机与行动的新手问题: 动作描述发生了某事的事实,但没有具体说明 应用程序的状态如何随响应而变化 及 给定相同的参数,reducer应该计算下一个状态和 归还它。没有意外。没有副作用。没有API调用。没有突变。 只是计算一下 所以,如果我们考虑下面的场景: 用户可以在地图上放置点,并获得这些点之间的路线 当用户第一次单击地图时,这是他的起点。当他第二次点击时——这是他的终点。后续单击可在上一点和终点位置之间添加点 添加每个点后(第一个点除外),必须计算新点和上一个点之间的路线。因此,如果我
所以,如果我们考虑下面的场景:
- 新点不会放置在列表的末尾
- 新路线必须计算到终点
// 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))
})
}
}
}
对我来说,这与“但不要指定应用程序的状态如何更改”相矛盾,因为从操作开始,我指定在何处插入我的新点。我可以计算减速器中的插入位置,但如何获取终点的路线数据?
这里的正确方法是什么?假设我们有一个接受两点的
calculateRoute
函数,并返回一个解决两点之间路由的承诺
首先,让我们创建一个简单的动作创建者,以便我们知道我们的点被正确存储:
let addPoint = (point, index) => {
return {
type: 'ADD_POINT',
point: point,
index: index
}
}
然后,让我们在减速器中处理此操作:
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中也有一个routes列表,因此让我们扩展它以支持:
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;
}
}
而动作创建者将是:
let updateRoutes = (routes) => {
return {
type: 'UPDATE_ROUTES',
routes: routes
}
}
请注意,我们正在覆盖整个routes集合。目前还可以,但在生产系统中,您可能需要对其进行一些优化
现在我们需要写一些逻辑。我将假设一个方便的假设,我们有一个计算器outes
,它获取点的集合,并返回一个承诺,该承诺解析相应路线的列表,每个路线将是一个对象,包含两个点和实际路线。话虽如此,我们的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));
};
};
在我看来,这是更好的方式
当然,假设我们有一个神奇的
calculatoroutes
函数并不是一个严肃的假设,尽管以优化的方式实现这个函数并不是一项非常困难的任务(这意味着实际上只向服务器发送我们以前没有计算过的路由,等等)但这只是逻辑,而不是应用程序的状态,因此,只要您保持存储和还原器定义的“契约”,您就可以自由地以任何方式实现它
希望这对您有所帮助。那么看看您建议的
addPointAndUpdateRoutes=(点,索引)
和addPoint(点,索引)
签名-索引计算是否移动到用户界面?但是如果我可以通过点击地图手动输入坐标来添加一个点呢?。关于calculateRoutes
如果我得了100分怎么办?还是200?每次在整个列表中循环,检查一些缓存(还有另一个状态…)以及用户是否总是可以从头开始?然后我必须使缓存失效..我想你可以在UI中计算索引
,我的意思是在两个现有点之间添加一个点可以在UI中非常清晰地表达出来。关于calculateRoutes
-您需要有一个记忆功能,这意味着如果它已经计算了路由,它会立即返回路由,此实现需要在您的客户端和服务器上实现。如果一个用户有100分、1000分甚至5000分,这并不重要,只要不是真正计算出来的,它就会发生得很快。无论如何,如果你支持100个点,你就必须计算它们,不管UI实现如何。我的观点是,我可能有两个、四个或五个不同的地方可以添加点。并且逻辑保持不变——如果第一个点添加,第二个点添加,如果第三个点添加在最后一个点之前,等等。所以这个逻辑不应该在每个UI组件中重复。如果我使用智能计算器的方法,这可以在减速机中完成(但是否正确?)。或者我为什么要分开储存路线——它们不能没有一个点。如果无法到达某个点,则该点可能没有路线,这会产生副作用,即终点也可能无法到达。可以在五个不同的位置添加一个点,但所有这些位置都应引用相同的逻辑,不能在减速器中<代码>计算器输出与减速机也没有任何关系,只是减速机存储其输出。点是一个实体,路线是一个完全不同的实体,正好包含两个点。