Database 如何同步web客户端和数据库?在客户端创建实体并处理ID
背景 比如说,我正试图保留一个大的项目列表,其中一个web UI提供了添加新项目的可能性。 我的体系结构是一个web UI React/Redux+一个数据库/服务器图 现在,当我添加一个新项时,我所能做的就是运行create查询,用它的数据库ID返回该项,并将该项存储在一个对象中,该对象按ID索引所有项 问题:网络可能很慢/离线,请求可能会失败,我仍然希望能够立即向用户显示列表中新创建的项目,并稍后与数据库同步 问题: 我需要一个解决方案,允许我首先在本地创建项,然后在响应返回时用数据库ID替换本地ID。Database 如何同步web客户端和数据库?在客户端创建实体并处理ID,database,redux,synchronization,Database,Redux,Synchronization,背景 比如说,我正试图保留一个大的项目列表,其中一个web UI提供了添加新项目的可能性。 我的体系结构是一个web UI React/Redux+一个数据库/服务器图 现在,当我添加一个新项时,我所能做的就是运行create查询,用它的数据库ID返回该项,并将该项存储在一个对象中,该对象按ID索引所有项 问题:网络可能很慢/离线,请求可能会失败,我仍然希望能够立即向用户显示列表中新创建的项目,并稍后与数据库同步 问题: 我需要一个解决方案,允许我首先在本地创建项,然后在响应返回时用数据库ID替
我该怎么做呢?最简单的方法是在对象上创建某种localKey字段,然后像往常一样将其添加到存储中
每当后端完成处理并出现事件时,使用localKey查找本地副本并替换它,而不是将新对象添加到存储中。结果表明,这是我遇到的代码组织/Redux问题 我不知道如何实施Tobe O建议的某种解决方案,因为: 如果我先触发请求,我就无法附加localID并将其与跟踪它的响应一起取回 如果我首先在reducer中创建了本地项,我就无法将localID传递给执行请求并存储从响应中提取的项的部件/我认为从reducer中抛出操作是不好的做法 我所做的似乎效果不错的是: 触发由后台工作人员拾取的第一个动作 worker创建localID并将其传递给两个新操作:一个用于创建本地项,另一个用于向数据库触发请求 下面是我的代码解决方案,Redux+Sagas+Kea+是一个经典的待办事项列表示例:
export default kea({
path: () => ["kea", "taskStore"],
actions: () => ({
addTask: title => ({ title }),
storeLocalTask: (localId, title) => ({ localId, title }),
storeDBTask: (localId, task) => ({ localId, task }),
setTasks: tasks => ({ tasks }),
fetchTasks: () => ({})
}),
*start() {
const { fetchTasks } = this.actions;
yield put(fetchTasks());
},
reducers: ({ actions }) => ({
indexedTasks: [
{},
PropTypes.object,
{
[actions.setTasks]: (state, payload) => indexById(payload.tasks),
[actions.storeLocalTask]: (state, payload) => {
const { localId, title } = payload;
return { ...state, [localId]: { localId, title } };
},
[actions.storeDBTask]: (state, payload) => {
const { localId, task } = payload;
const { [localId]: _dispose_, ...rest } = state;
return { ...rest, [task.id]: task };
}
}
]
}),
takeLatest: ({ actions, workers }) => ({
[actions.fetchTasks]: workers.fetchTasks,
[actions.addTask]: workers.createTask
}),
workers: {
*fetchTasks() {
const res = yield db.query({
query: gql`
query {
allTasks {
id
title
}
}
`
});
const { setTasks } = this.actions;
yield put(setTasks(res.data.allTasks));
yield delay(1000);
},
*createTask(action) {
const { storeLocalTask, storeDBTask } = this.actions;
const { title } = action.payload;
const localId = randomString();
yield put(storeLocalTask(localId, title));
const res = yield db.mutate({
mutation: gql`
mutation($title: String!) {
createTask(title: $title) {
id
title
}
}
`,
variables: { ...action.payload }
});
yield put(storeDBTask(localId, res.data.createTask));
yield delay(1000);
}
}
});
事实证明,这是我遇到的代码组织/重复使用问题。见下面我的答案。现在我的问题不像我想的那么清楚/有用,我应该删除它吗?或者以某种方式改进它?