如何同步Web客户端和数据库?在客户端创建实体并处理ID

时间:2017-10-28 19:51:25

标签: database redux synchronization

背景

假设我正在尝试保留大量项目,使用Web UI提供添加新项目的可能性。 我的架构是一个Web UI(React / Redux)+一个数据库/服务器(Graph.cool)。

现在当我添加一个新项目时,我可以做的只是运行“创建”查询,获取带有数据库ID的项目,并将项目存储在按ID索引所有项目的对象中。

问题:网络可能很慢/脱机,请求可能会失败,我仍然希望能够立即向用户显示列表中新创建的项目,并稍后与数据库同步。

问题

我需要一个允许我首先在本地创建项目的解决方案,然后在响应返回时将本地ID替换为数据库ID。 我怎么能这样做?

2 个答案:

答案 0 :(得分:1)

最简单的方法是在对象上创建某种localKey字段,并按照惯例将其添加到商店。

每当后端完成处理并且事件进入时,不是将新对象添加到商店,而是使用localKey查找本地副本,并替换它。

答案 1 :(得分:0)

原来这是我所拥有的代码组织/ Redux的问题。

我无法弄清楚如何实施Tobe O建议的某种解决方案,因为:

  • 如果我首先解雇了请求,我无法附加localID并将其恢复为跟踪它的响应
  • 如果我首先在reducer中创建本地项目,我无法将localID传递给执行请求的部分并存储从响应中提取的项目/我认为这是一种不好的做法来自减速机

我所做的事情似乎运作良好,是:

  • 触发第一个被背景中的工人拾取的动作
  • 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);
    }
  }
});