React18 的 Suspense 与之前的 Suspense 有什么不同之处?

首先

这篇文章是关于在Qiita株式会社的内部学习会上发布的React18中Suspense新功能的总结文章!

如果方便的话,请阅读以下关于公司内部研讨会的文章!

 

悬疑

 

最近人们经常讨论的React18的新功能之一是Suspense,但其实Suspense本身是在React16.6中新增的功能。那么到底有什么新的能力呢?

本文将简要回顾在 React16.6 中添加的 Suspense,并介绍了在 React 18 及以后版本的 Suspense 中可以实现的功能。

React 16.6 之前的 Suspense

当初引入Suspense(悬念)时,官方只支持通过使用Suspense来实现Dynamic import(动态导入)。详情请参考下文所述的文章。

 

总结起来,可以归纳如下。

动态引入的悬念
通过代码分割将组件拆分后,在引入过程中显示回退界面,直到引入完成后再显示悬念内部。

React 18之后的Suspense。

React 18 中,Suspense 经过官方版发布。虽然 RFC 中具体介绍了与 React 16.6 的差异,但细节将被省略。

 

本文介绍了特别是在React 18中正式支持的除了动态导入(Dynamic import)之外的使用方法(Suspense for Data Fetching)。在撰写本文时,我参考了@uhyo先生的许多文章。

 

数据获取的悬念

简单来说,就是这样的感觉。

在进行数据获取时,显示fallback,当异步处理完成后,显示Suspense内的内容。

使用 Promise 来检测”非同步处理进行中”和”非同步处理完成”。

以下是处理的顺序。

Suspsense を含むコンポーネントが render される際に、 Suspsense 内の render が試される

Promise が throw された場合

Promise を実行して完了するまで fallback を表示
Promise が完了したら、再び Suspsense 内の render を試す

問題なければ render 結果を表示

「承諾を放棄する」は特に重要なポイントですね。

实施示例

使用这个新的Suspense,需要创建一个抛出Promise的组件。
首先,抛出一个Promise,并准备一个fetchPosts函数,当这个Promise解决时,返回结果值。

我参考了以下网站的示例。

 

let status = "pending"
let result

function fetchPosts(userId) {
  let url = `https://jsonplaceholder.typicode.com/posts${userId ? "?userId=" + userId : ""}`
  let fetching = fetch(url)
    .then((res) => res.json())
    .then((success) => {
      status = "fulfilled"
      result = success
    })
    .catch((error) => {
      status = "rejected";
      result = error;
    });

  return () => {
    if (status === "pending") {
      throw fetching // Promise を throw
    } else if (status === "rejected") {
      throw result
    } else if (status === "fulfilled") {
      return result // Promise が解決したら、取得したデータを返す
    }
  }
}

然后,创建使用fetchPosts的组件,并在Suspense中使用。

const Posts = () => {
  const userId = JSON.parse(localStorage.getItem("authenticatedUser"))?.id
  const posts = fetchPosts(userId)

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: "10px" }}>
      {posts.map((post, idx) => (
        <Post post={post} key={idx} />
      ))}
    </div>
  )
}

const MyPosts = () => {
  return (
    <Suspense fallback={<Spinner />}>
      <Posts />
    </Suspense>
  )
}

我认为实现的方式会是这样。通过这样做,就可以像之前解释的那样,在异步处理执行期间显示fallback,在异步处理完成后显示Suspense内的内容。这个过程可以通过使用Suspense来实现。

(再次提醒) 处理顺序

Suspsense を含むコンポーネントが render される際に、 Suspsense 内の render が試される

Promise が throw された場合

Promise を実行して完了するまで fallback を表示
Promise が完了したら、再び Suspsense 内の render を試す

問題なければ render 結果を表示

当能够使用悬疑模式进行异步处理,会有什么变化?

如果您想要实现异步处理以前,您需要像以下这样使用 useState 来管理状态,并使用 useEffect 进行操作。

function Posts() {
  const [isLoading, setIsLoading] = useState(true)
  const [posts, setPosts] = useState([])

  useEffect(() => {
    setIsLoading(true)
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((response) => response.json())
      .then((json) => setPosts(json))
      .finally(() => setIsLoading(false))
  }, [])

  if (isLoading) { return <Spinner /> }

  return (
    <div style={{ display: "flex", flexWrap: "wrap", gap: "10px" }}>
      {posts.map((post, idx) => (
        <Post post={post} key={idx} />
      ))}
    </div>
  )
}

对于这一点,使用 Suspense 可以像之前展示的实现示例那样,无需使用 useState 或 useEffect 进行实现。但是,需要一个类似 fetchPosts 的函数,该函数“首先会抛出一个 Promise,并在该 Promise 解决后返回结果值”。据说有以下类型的包装器,这样的实现会更容易。(参考:https://blog.logrocket.com/react-suspense-data-fetching/)

function wrapPromise(promise) {
  let status = 'pending'
  let response

  const suspender = promise.then(
    (res) => {
      status = 'success'
      response = res
    },
    (err) => {
      status = 'error'
      response = err
    },
  )

  const read = () => {
    switch (status) {
      case 'pending':
        throw suspender
      case 'error':
        throw response
      default:
        return response
    }
  }

  return { read }
}

export default wrapPromise

我认为在各种场景中都会出现异步获取数据,然后在加载后渲染的模式,所以我希望未来通过Suspense可以提高这种模式的开发体验!

请提供参考资料。

    • 公式サイト

React v18.0 – React

RFC

rfcs/text/0213-suspense-in-react-18.md at main · reactjs/rfcs

偉大なる uhyo さんの記事

ReactのSuspense対応非同期処理を手書きするハンズオン
React 18に備えるにはどうすればいいの? 5分で理解する – Qiita
Promiseをthrowするのはなぜ天才的デザインなのか – Qiita

その他

How to use Suspense for data fetching in React – DEV Community
Experimental React: Using Suspense for data fetching – LogRocket Blog

广告
将在 10 秒后关闭
bannerAds