React Redux Store Layout:如何处理 "add item" 请求的 "pending" 状态?
React Redux Store Layout: How to handle "pending" state of "add item" request?
示例商店:
{
todos: {
byId: {
"1": { id: "1", title: "foo" },
"2": { id: "2", title: "bar" }
},
allIds: ["2", "1"] // ordered by `title` property
}
}
现在用户想要添加一个新的 Todo 条目:
dispatch({
type: 'ADD_TODO_REQUEST',
payload: { title: "baz" }
})
这会触发一些 API 请求:POST /todos
。只要没有 没有 响应(success
或 error
),请求的状态就是 pending
。这也意味着,我还没有 id
新创建的 Todo 条目。
现在我已经想将它添加到商店(并展示它)。但是当然我不能将它添加到byId
和allIds
,因为它还没有id
。
问题 1:我应该如何更改商店的布局才能实现这一点?
响应到达后,有两种可能:
success
:更新商店并设置新Todo Entry的id
属性。使用 dispatch({type:'ADD_TODO_SUCCESS', payload: response.id})
.
error
:从商店中删除新的 Todo 条目。使用 dispatch({type:'ADD_TODO_ERROR', payload: ???})
现在这两个动作的化简器必须以某种方式在商店中找到相应的元素。但是它没有标识符。
问题 2:如果没有 id
,我如何在商店中找到该商品?
附加信息:
- 我正在使用 react with redux-saga
- 应该可以同时有多个并发的
ADD_TODO_REQUEST
运行。尽管商店中必须可以有多个 pending
Todo 条目。 (例如,如果网络连接真的很慢并且用户只是输入 "title1" 并点击 "add" 按钮,则 "title2" 和 "add"、"title3" 和 "add".) 虽然在请求未决时 不可能 禁用 AddTodo
组件。
您是如何解决应用程序中的此类问题的?
编辑:还有更多:
相同的功能应该可用于 "updating" 和 "deleting" Todo 条目:
当用户编辑 Todo 条目然后点击 "save" 按钮时,该项目也应该处于 pending
状态,直到响应到达。如果出错,则必须将旧版本的数据放回存储中(无需向服务器请求)。
当用户点击"delete"时,该项目将立即消失。 但是如果服务器响应错误,则应将该项目放回列表中。
如果出现错误响应,这两个操作都应该恢复以前的数据。
我找到了一个简单的解决方案。 但我确信还有其他可能性甚至更好的解决方案。
将 Todo 条目保存在 2 个单独的集合中:
{
todos: {
byId: {
"1": { id: "1", title: "foo" },
"2": { id: "2", title: "bar" }
},
allIds: ["2", "1"],
pendingItems: [
{ title: "baz" },
{ title: "42" }
]
}
}
现在我可以在商店里找到它们 "by reference"。
// handle 'ADD_TODO_REQUEST':
const newTodoEntry = { title: action.payload.title };
yield put({ type: 'ADD_TODO_PENDING', payload: newTodoEntry });
try {
const response = yield api.addTodoEntry(newTodoEntry);
yield put({ type: 'ADD_TODO_SUCCESS', payload: { id: response.id, ref: newTodoEntry } });
} catch(error) {
yield put({ type: 'ADD_TODO_ERROR', payload: newTodoEntry });
}
reducer 看起来像这样:
case 'ADD_TODO_PENDING':
return {
..state,
pendingItems: // add action.payload to this array
}
case 'ADD_TODO_SUCCESS':
const newTodoEntry = { ...action.payload.ref, id: action.payload.id };
return {
..state,
byId: // add newTodoEntry
allByIds: // add newTodoEntry.id
pendingItems: // remove action.payload.ref from this array
}
case 'ADD_TODO_ERROR':
return {
..state,
pendingItems: // remove action.payload.ref from this array
}
有2个问题:
减速器必须使用对象引用。 reducer 不允许从 ADD_TODO_PENDING
.
的动作负载创建自己的对象
商店中的 Todo 条目不容易排序,因为有两个不同的集合。
有 2 个解决方法:
- 使用客户端生成的
uuid
s 仅当项目处于 pending
状态时才存在。这样,客户可以很容易地跟踪一切。
2.
a) 将某种 insertAtIndex
属性 添加到待定项目中。然后 React 组件代码可以合并这两个集合并以自定义顺序显示混合数据。
b) 将项目分开。例如,服务器数据库中已保存项目列表顶部和下方的未决项目列表。
示例商店:
{
todos: {
byId: {
"1": { id: "1", title: "foo" },
"2": { id: "2", title: "bar" }
},
allIds: ["2", "1"] // ordered by `title` property
}
}
现在用户想要添加一个新的 Todo 条目:
dispatch({
type: 'ADD_TODO_REQUEST',
payload: { title: "baz" }
})
这会触发一些 API 请求:POST /todos
。只要没有 没有 响应(success
或 error
),请求的状态就是 pending
。这也意味着,我还没有 id
新创建的 Todo 条目。
现在我已经想将它添加到商店(并展示它)。但是当然我不能将它添加到byId
和allIds
,因为它还没有id
。
问题 1:我应该如何更改商店的布局才能实现这一点?
响应到达后,有两种可能:
success
:更新商店并设置新Todo Entry的id
属性。使用dispatch({type:'ADD_TODO_SUCCESS', payload: response.id})
.error
:从商店中删除新的 Todo 条目。使用dispatch({type:'ADD_TODO_ERROR', payload: ???})
现在这两个动作的化简器必须以某种方式在商店中找到相应的元素。但是它没有标识符。
问题 2:如果没有 id
,我如何在商店中找到该商品?
附加信息:
- 我正在使用 react with redux-saga
- 应该可以同时有多个并发的
ADD_TODO_REQUEST
运行。尽管商店中必须可以有多个pending
Todo 条目。 (例如,如果网络连接真的很慢并且用户只是输入 "title1" 并点击 "add" 按钮,则 "title2" 和 "add"、"title3" 和 "add".) 虽然在请求未决时 不可能 禁用AddTodo
组件。
您是如何解决应用程序中的此类问题的?
编辑:还有更多:
相同的功能应该可用于 "updating" 和 "deleting" Todo 条目:
当用户编辑 Todo 条目然后点击 "save" 按钮时,该项目也应该处于
pending
状态,直到响应到达。如果出错,则必须将旧版本的数据放回存储中(无需向服务器请求)。当用户点击"delete"时,该项目将立即消失。 但是如果服务器响应错误,则应将该项目放回列表中。
如果出现错误响应,这两个操作都应该恢复以前的数据。
我找到了一个简单的解决方案。 但我确信还有其他可能性甚至更好的解决方案。
将 Todo 条目保存在 2 个单独的集合中:
{
todos: {
byId: {
"1": { id: "1", title: "foo" },
"2": { id: "2", title: "bar" }
},
allIds: ["2", "1"],
pendingItems: [
{ title: "baz" },
{ title: "42" }
]
}
}
现在我可以在商店里找到它们 "by reference"。
// handle 'ADD_TODO_REQUEST':
const newTodoEntry = { title: action.payload.title };
yield put({ type: 'ADD_TODO_PENDING', payload: newTodoEntry });
try {
const response = yield api.addTodoEntry(newTodoEntry);
yield put({ type: 'ADD_TODO_SUCCESS', payload: { id: response.id, ref: newTodoEntry } });
} catch(error) {
yield put({ type: 'ADD_TODO_ERROR', payload: newTodoEntry });
}
reducer 看起来像这样:
case 'ADD_TODO_PENDING':
return {
..state,
pendingItems: // add action.payload to this array
}
case 'ADD_TODO_SUCCESS':
const newTodoEntry = { ...action.payload.ref, id: action.payload.id };
return {
..state,
byId: // add newTodoEntry
allByIds: // add newTodoEntry.id
pendingItems: // remove action.payload.ref from this array
}
case 'ADD_TODO_ERROR':
return {
..state,
pendingItems: // remove action.payload.ref from this array
}
有2个问题:
减速器必须使用对象引用。 reducer 不允许从
ADD_TODO_PENDING
. 的动作负载创建自己的对象
商店中的 Todo 条目不容易排序,因为有两个不同的集合。
有 2 个解决方法:
- 使用客户端生成的
uuid
s 仅当项目处于pending
状态时才存在。这样,客户可以很容易地跟踪一切。
2.
a) 将某种
insertAtIndex
属性 添加到待定项目中。然后 React 组件代码可以合并这两个集合并以自定义顺序显示混合数据。b) 将项目分开。例如,服务器数据库中已保存项目列表顶部和下方的未决项目列表。