The Suspense is Killing Redux

20 天前

译文已获得原作者授权,转载时请附上原文链接 https://medium.com/@ryanflorence/the-suspense-is-killing-redux-e888f9692430

在最近举办的 workshop 上我一直考虑这个问题:

Suspense 会杀死 Redux 吗?

不得不说上面的表达方式过于粗鲁,但我认为 Suspense 即将取代 Redux。

React Suspense 主要用来处理异步数据请求时的页面渲染,这方面是 Redux 未曾涉及的,但为了做到这一点,Suspense 需要去接管客户端的数据逻辑,而这也正是 Redux 一直以来所擅长的。

如果你还没有听说过 React Suspense,可以先花点时间听下 Dan Abramov 在 JSConf 冰岛上的关于 React Suspense 的分享

Suspense 的基本用法

使用 Suspense 分成三个部分:缓存,资源请求和组件。

缓存示例:

carbon.ab719b5fa268.png

资源请求示例:

1.50887b6afab1.png

它仅需要接受一个返回 promise 的函数。如下是一个使用了上方的资源请求和缓存的组件示例:

2.3ff52f357fb0.png

瞧,这就是 Suspense

这个应用的渲染过程的步骤如下:

  1. React 开始渲染(在内存中)。
  2. 调用 InvoicesResource.read()
  3. invoiceId 对应的缓存数据不存在,从而触发 createResource 函数的调用,发送异步请求去获取数据。
  4. 然后,缓存会 throw 第 3 步中返回的 promise。(这里能 throw 的除了异常,也可以是其他东西,甚至可以是 window 对象)这个 promise 被 React 捕获之后,会暂停渲染过程。
  5. React 等待异步请求结果(等待 promise resolve)。
  6. 异步请求完成(promise resolve)。
  7. React 尝试再次渲染 Invoices(在内存中)。
  8. 再次调用 InvoicesResource.read()
  9. 此时已有数据缓存,ApiResource.read() 同步返回数据。
  10. React 渲染出页面。

上方的步骤中,我们将异步的资源请求逻辑当做同步函数调用,用起来非常炫酷。

而关于资源请求时如何处理占位符和 Spinner,就不在这里展开讨论,我们只需要知道:React 将会维持在老的页面,直到新的数据加载完成,当资源请求时间超时,可以先去渲染占位符。

那么在 Redux 中又是如何工作呢?

在 redux 中执行相同的流程的话,它会是这样的:

3.260c1f4bb472.png

对比一下

它们的工作方式不同,但也有些部分是相同的:

  • store → cache
  • mapStateToProps → Resource.read()
  • action → function passed to resource

reducer 和 dispatch 函数不复存在,因为已不再需要通过 action 来更新 state,而是直接从资源请求中读取数据。当(缓存中)没有数据时,会暂停渲染并等待数据请求返回。在某种程度上,cache + resources 同时充当了 dispatch,reducers 和 actions 的作用。

另外使用 Suspense 时不需要使用生命周期 hook 函数。当 invoiceId 改变而触发 Invoice 重新渲染时,若该 invoiceId 对应的缓存数据为空,则会自动发送新请求去获取数据,这比在生命周期 hook 函数中监听 props 的变化再 dispatch 一个 action 要简单得多。

依我看来,无论你之前是习惯使用 Redux store 还是组件层的 local state,切换到 Suspense 都会毫无压力。

因此,如果你使用 Redux 主要用来获取服务端数据并渲染的话,那么 Suspense 完全可以替换掉 Redux。我会用 Suspense,因为它能让代码更简单,更好处理加载中的状态。

缓存失效怎么办

createCache 的第一个参数就是一个用于处理失效的函数。我测试下来发现,一旦缓存数据更新,页面就能根据新的数据直接重新渲染。这能客户端数据缓存和服务器数据保持同步,看起来是不是很酷。

此外,Suspense 处理页面更新的方式感觉很棒:新页面在内存中渲染时,旧页面仍然存在并且是可交互的,当数据更新后,页面将使用新数据(来自服务端)重新渲染,要知道通常服务端渲染也是这样工作的,这种方式也会让同步客户端和服务器数据的中间层的存在失去意义。

Suspense 无法取代一切

有些人正在使用 Redux 做更复杂的事情(比如将状态保持与 API 同步的同时也存到 localStorage 中 ),Suspense 也不能取代 Redux 的所有应用场景。

但是,依我看来,我所接触的大多数正在使用 Redux 的人,仅仅是用来获取服务器端的资源数据,如果你也是其中一员的话,我想你会爱上 Suspense 的易用性和用户体验。

参加 workshop

本文作者 Ryan Florence 正在美国地区举办 React 相关 workshop,点击查看 workshop 城市和日期

译者后记

本文中提到的例子,目前已可以使用最新发布的 react v16.6 版本体验,react-cache 使用 canary 版本(react-cache API 极不稳定,最新发布的 2.0.0-alpha.0 API 已改,请慎重使用 😂),完整示例代码可参考 https://github.com/foryuki/suspense-sample

1
推荐阅读