是否有一种行之有效的方法可以立即更新本地状态,而无需等待 React/Redux 中的 API 响应?

Is there a well-established way to update local state immediately without waiting for an API response in React/Redux?

TL;DR:是否有一些著名的解决方案使用 React/Redux 能够提供快速且立即响应的 UI,同时保持 API/database 高达可以优雅地处理失败的 API 请求的更改日期?


我希望使用 https://github.com/atlassian/react-beautiful-dnd 实现具有“卡片视图”的应用程序,用户可以在其中拖放卡片以创建组。当用户创建、修改或分解组时,我想确保 API 与用户的操作保持同步。

但是,我不想在更新 UI.

之前必须等待 API 响应来设置状态

我进行了广泛的搜索,但不断发现诸如 https://redux.js.org/tutorials/fundamentals/part-6-async-logic 之类的东西,这表明 API 的响应应该更新状态。

例如:

export default function todosReducer(state = initialState, action) {
  switch (action.type) {
    case 'todos/todoAdded': {
      // Return a new todos state array with the new todo item at the end
      return [...state, action.payload]
    }
    // omit other cases
    default:
      return state
  }
}

作为一般概念,这对我来说总是很奇怪,因为它是本地应用程序告诉 API 需要更改的内容;我们显然在服务器响应之前就已经有了数据。情况可能并非总是如此,例如创建一个新对象并希望服务器指定某种新的“唯一 ID”,但似乎有一种方法可以在服务器完成后“填空”对任何缺失的数据做出响应。在 UPDATE 与 CREATE 的情况下,服务器没有告诉我们我们不知道的任何信息。

这可能适用于小型轻量级应用程序,但如果我查看 API 平均 500-750 毫秒范围内的响应,用户体验将绝对是垃圾。

创建两个操作很简单,一个处理更新状态,另一个触发 API 调用,但是如果 API returns 出现错误或网络请求失败,我们需要恢复?

我测试了 Trello 如何通过切断我的网络连接并创建一张新卡片来实现这种事情。它在提交后立即急切地创建卡片,然后在意识到无法更新服务器时删除卡片。这就是我要找的那种行为。

我研究了 https://redux.js.org/recipes/implementing-undo-history,它提供了一种“倒回”状态的方法,但是为了我的目的能够实现它需要假设后续的 API 调用都在相同的情况下解析他们被调用的顺序 - 显然情况可能并非如此。

截至目前,我已经接受了这样一个事实,即我可能只需要遵循既定的有限模式,并锁定 UI 直到 API 请求完成,但我很乐意如果它存在于 React/Redux.

的世界中,则更好的选择

这里的关键词是“乐观更新”,这是一种通用模式,在假设任何 API 请求都会成功的情况下,使用给定的更改立即更新客户端上的“本地”状态。无论您在客户端使用什么实际工具来管理状态,都可以实施此模式。

如果网络请求失败,您可以定义和实施适当的更改。

您所说的方法称为“乐观”网络处理——假设服务器将接收并接受客户端正在执行的操作。这适用于您不需要服务器端验证来确定您是否可以创建或更新对象的情况。使用 React 和 Redux 也同样容易实现。

通常情况下,使用 React 和 Redux,更新流程如下:

  1. 组件调度一个异步操作创建者
  2. 异步操作创建者运行其副作用(调用服务器),并等待响应。
  3. 异步动作创建者,根据副作用的结果,调度一个动作来调用缩减器
  4. reducer 更新状态,重新渲染组件。

一些示例代码来说明(我假装我们在这里使用 redux-thunk):

// ... in my-component.js:
export default () => {
 const dispatch = useDispatch();

 useEffect(() => {
  dispatch(MyActions.UpdateData(someDataFromSomewhere));
 });

 return (<div />);
};

// ... in actions.js
export const UpdateData = async (data) => (dispatch, getStore) => {
  const results = await myApi.postData(data);

  dispatch(UpdateMyStore(results));
};

但是,您可以轻松翻转异步代码的运行顺序,只需不等待异步副作用解决。实际上,这意味着您无需等待 API 响应。例如:

// ... in my-component.js:
export default () => {
 const dispatch = useDispatch();

 useEffect(() => {
  dispatch(MyActions.UpdateData(someDataFromSomewhere));
 });

 return (<div />);
};

// ... in actions.js
export const UpdateData = async (data) => (dispatch, getStore) => {
  // we're not waiting for the api response anymore,
  // we just dispatch whatever data we want to our reducer
  dispatch(UpdateMyStore(data));
  myApi.postData(data);
};

最后一件事 -- 以这种方式做事,您 想要放置一些协调机制,以确保客户端确实知道服务器调用是否失败,并重试或通知用户等