如何在Redux中跟踪不同API调用的状态?

时间:2018-01-01 15:44:38

标签: javascript redux redux-observable

假设我有管理待办事项的应用程序,用户可以通过单击按钮添加新的待办事项 当用户单击该按钮时,将显示一个微调器,直到todo保存到后端或请求失败。

当按钮按下时,将调度一个名为 ADD_TODO_REQUEST 的动作,然后由一个执行HTTP请求以保存待办事项的redux-observable史诗拦截,并将调度 ADD_TODO_COMPLETE 操作或 ADD_TODO_FAILED 操作,具体取决于HTTP请求结果。

应用程序应在" add todo"旁边显示一个微调器。按钮。
为此,我的状态包含一个名为 isSaving 的标志,当HTTP请求挂起时将设置为true,并在HTTP请求完成时重置为false。

我的初始状态如下所示:

{
     todos: [],
     isSaving: false
}

当应用程序启动时,将要调度的第一个操作是 FETCH_TODO_REQUEST ,它将调用另一个API端点来获取所有待办事项。

应用程序应该再次显示一个微调器来通知用户正在下载待办事项,为此我已经为状态添加了另一个标记,名为 isFetching
这个新标志是必需的,因为如果我在添加待办事项时共享相同的标志,当用户只是添加待办事项时,我会为整个应用程序显示一个微调器。

我的初始状态现在看起来像这样:

{
     todos: [],
     isSaving: false,
     isFetching: false
}

这种方法对我来说很好,但如果用户也可以删除待办事项,我必须跟踪这个额外的HTTP请求状态,因此我需要添加另一个标志(可能称为 isDeleting )到国家。

请注意,我想在"添加按钮"旁边显示一个微调器。每个待删​​除的待办事项旁边都有一个微调器。这些旋转器都可以同时出现,这就是为什么单个标志不够而且我采用了这种方法。

在我可能有许多不同的并发API"动作"每个可能的要求我都需要一面旗帜 如果我还想显示错误,我现在需要为每个API提供两个属性" action" available:用于表示请求正在进行中,另一个用于保存错误对象。

这种方法的问题在于它看起来非常非常,详细。

是否有一种惯用且更智能的方式来跟踪并发http请求的状态? 为每个可能的http请求设置一个标记,触发相同的"实体"是否正确? ?

2 个答案:

答案 0 :(得分:1)

我已经看到很多处理这个问题的方法,都有各自的优点和缺点。

每个资源都有自己的状态

虽然没有规则,但通常建议redux用户将其状态模式建模为数据库。

这主要意味着您将存储由某个ID索引的资源。在这种情况下,您有一个待办事项列表,因此您的状态可能如下所示:

{
  todosById: {
    '1': {
      id: '1',
      content: 'Do something'
    },
    '2': {
      id: '2',
      content: 'Do another thing'
    }
  }
}

如果您想要所有待办事项的数组,只要您选择州,就可以动态创建:

const selectTodos = (state) => Object.values(state.todosById);
/*
  [{
    id: '1',
    content: 'Do something'
  }, {
    id: '2',
    content: 'Do another thing'
  }]
*/

const selectTodoIds = (state) => Object.keys(state.todosById)
// ['1', '2']

你为什么要这样做?一个原因是它使得通过ID查找内容非常容易,这在用户流程中经常需要;例如显示事物列表然后使用选择其中一个。

适用于您描述的情况的另一个原因是我们现在可以分别跟踪每个资源的状态。所以每个人都可以拥有自己的isFetchingisSaving等等。或者,如果你想保证资源只在一个状态中你可以使用枚举 - 但要小心它真的不可能同时抓取和保存!

{
  todosById: {
    '1': {
      id: '1',
      content: 'Do something',
      isFetching: false,
      isSaving: false
    },
    '2': {
      id: '2',
      content: 'Do another thing',
      isFetching: false,
      isSaving: false
    }
  }
}

interface Todo {
  id: string;
  content: string;
  isFetching: boolean;
  isSaving: boolean;

  // or using an enum. here are some possibilities:
  enum Status { Fetching, Saving, New, Prestine, Dirty }
  status: Status;
}

当您的用户直接访问其中一个资源时,这也很自然。例如如果他们可以导航到单个Todo,您可以在todosById中填充它并跟踪其状态。如果您以后还加载了待办事项列表,服务器的最新值将合并到我们之前加载的Todo中。

在处理新资源客户端的创建时,在将其保存到服务器之前,您可能不会拥有该ID。在那种特殊情况下,我有一个生成的临时ID,仅在客户端使用。例如'tmp-todo-1''tmp-todo-2'

{
  todosById: {
    'tmp-todo-1': {
      id: 'tmp-todo-1',
      content: 'A new todo that has never been saved yet',
      status: Status.New
    },
    '1': {
      id: '1',
      content: 'A todo that has been created but has unsaved changes',
      status: Status.Dirty
    },
    '2': {
      id: '2',
      content: 'Do another thing',
      status: Status.Prestine
    }
  }
}

分开状态

更复杂的资源请求管理的另一个选择是使其完全独立,并且不知道其获取/保存/等资源。

类似的东西:

{
  meta: {
    '1': {
      isFetching: false,
      isSaving: false
    },
    '2': {
      isFetching: false,
      isSaving: false
    }
  },
  todosById: {
    '1': {
      id: '1',
      content: 'Do something'
    },
    '2': {
      id: '2',
      content: 'Do another thing'
    }
  }
}

这样做的好处是不需要将资源本身与服务器上未包含的仅客户端状态合并,例如isFetching

这有很多种,其中一些人创建了类似redux-resource的库,虽然我没有使用过那个特定的库,所以我不能说太多。我使用自己制作的自定义抽象。

答案 1 :(得分:0)

为什么商店里没有isBusy个值?然后你的Reducer根据ADD_TODO_..DELETE_TO_DO_..等来切换。假设您只显示一个忙碌指示符。