首页 > 解决方案 > 是否有一种成熟的方法可以立即更新本地状态,而无需等待 React/Redux 中的 API 响应?

问题描述

TL;DR:是否有一些众所周知的解决方案使用 React/Redux 来提供快速响应的 UI,同时通过可以优雅地处理失败的 API 请求的更改使 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 的情况下,服务器没有告诉我们我们不知道的情况。

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

创建两个操作很简单,一个用于处理更新状态,另一个用于触发 API 调用,但如果 API 返回错误或网络请求失败并且我们需要恢复,会发生什么情况?

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

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

到目前为止,我正在接受这样一个事实,即我可能只需要遵循既定的有限模式,并锁定 UI 直到 API 请求完成,但如果它存在于 React/Redux 的世界中,我会喜欢一个更好的选择.

标签: javascriptreactjsreduxsetstate

解决方案


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

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

  1. 组件调度异步操作创建者
  2. 异步操作创建者运行其副作用(调用服务器),并等待响应。
  3. 异步动作创建者,带着副作用的结果,分派一个动作来调用reducer
  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);
};

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


推荐阅读