Slide 1

Slide 1 text

React 19を 概念から理解する 2024-05-29うひょさんに聞く! React 19アップデートの勘所 #React19_Findy

Slide 2

Slide 2 text

発表者紹介 uhyo 株式会社カオナビ フロントエンドエキスパート ずっとTypeScriptとReactで仕事している。 好きなReactの機能はやっぱりSuspense。

Slide 3

Slide 3 text

React 19 RC 4月25日にBeta、 5月15日にRCが 公開。あわせて ブログも公開された。 https://react.dev/blog/2024/04/25/react-19

Slide 4

Slide 4 text

This Talk React 19 RCのブログ記事や、すでに公開 されているReactのドキュメントをベースに React 19の機能を解説。 具体的なAPIを細かく見ていくというよりは、 React 19時代に重要になる大まかな概念を 理解することを目指す。

Slide 5

Slide 5 text

Reactのこれまで React 0.14 … 神話の時代 React 15 … クラスコンポーネント全盛期 React 16 … フックの時代(正確には16.8から) React 17 … No New Features React 18 … Suspenseの時代

Slide 6

Slide 6 text

React 19は何の時代になる?

Slide 7

Slide 7 text

React 19は何の時代になる?

Slide 8

Slide 8 text

React 19は何の時代になる? React 0.14 … 神話の時代 React 15 … クラスコンポーネント全盛期 React 16 … フックの時代(正確には16.8から) React 17 … No New Features React 18 … Suspenseの時代 React 19 … Actionsの時代

Slide 9

Slide 9 text

Actionsとは?

Slide 10

Slide 10 text

Actionsとは?

Slide 11

Slide 11 text

Actionsとは? By convention, functions that use async transitions are called “Actions”. 非同期トランジションを使う関数のことを、 慣例的に “Actions” と呼ぶことにします。

Slide 12

Slide 12 text

Actionsとは? By convention, functions that use async transitions are called “Actions”. 非同期トランジションを使う関数のことを、 慣例的に “Actions” と呼ぶことにします。 ?

Slide 13

Slide 13 text

余談: Reactのドキュメントについて より分かりやすくActionsを説明したいところだが、 発表者の経験的に、Reactの新概念は めちゃくちゃ調べたり実験したりして自分なりの 素晴らしい説明が書けたと思ったら、 公式ドキュメントに同じことが最初から書いてあった ということがよくある。

Slide 14

Slide 14 text

余談: Reactのドキュメントについて なので、公式の情報に正面から向き合い、 書いてあることを全部妥協せずに理解するのが Reactの新概念を深く正確に理解する最短ルート である。 (現時点ではまだドキュメントの更新が追いついていない ところもありそうなので、実験と気合で補いましょう)

Slide 15

Slide 15 text

Actionsとは? By convention, functions that use async transitions are called “Actions”. 非同期トランジションを使う関数のことを、 慣例的に “Actions” と呼ぶことにします。

Slide 16

Slide 16 text

非同期トランジションとは トランジションという概念は、React 18ですでに 導入されていた。(useTransition) 非同期の部分はReact 19の新しい概念である。 ページ遷移とかで使われるView Transition APIは関係ないので 気を付けよう! CSSのtransitionも関係ないです

Slide 17

Slide 17 text

トランジションの復習 (React 18でのトランジション) const [isPending, startTransition] = useTransition(); // イベントハンドラとかで startTransition(() => { setIsOpen(true); // 何らかのステート更新 });

Slide 18

Slide 18 text

トランジションの復習 startTransition(() => { setIsOpen(true); // 何らかのステート更新 }); startTransitionのコールバック内で行われた ステート更新はトランジションとなり、 優先度が低いステート更新として扱われる。

Slide 19

Slide 19 text

トランジションの性質 普通のステート更新: 更新後のレンダリング結果をすぐに・絶対に描画する。 レンダリングに時間がかかる場合、完了するまで 次のステート更新はできない。 優先度が低いステート更新(トランジション): レンダリングに時間がかかる場合は描画を遅延できる。 別のステート更新で割り込んだり、ステートを 再度更新してレンダリングをキャンセルしたりできる。

Slide 20

Slide 20 text

isPendingの挙動 const [isPending, startTransition] = useTransition(); トランジションの場合、ステート更新の結果が即座にUIに 反映されないこともある。その間、isPendingがtrue で レンダリングされる。 (トランジションのステート更新の反映はまだされずに、 isPendingだけがtrueになる)

Slide 21

Slide 21 text

Suspenseとトランジションの関係 トランジションのステート更新によって サスペンドが発生した場合も、レンダリングに 時間がかかる場合に準じた扱いとなる。 ステート更新後のUI(=Suspenseのfallback)が 出る代わりに、更新前のUIを維持したままで isPendingがtrueになる。 (優先度の低いステート更新なので更新前のUIを維持することが許容される)

Slide 22

Slide 22 text

Suspenseとトランジションの雑な例 const [userId, setUserId] = useState(0); const userProfile = use(dataSource.getUser(userId)); // … startTransition(() => { setUserId(123); // ステート更新したらサスペンド発生 });

Slide 23

Slide 23 text

React 18までのトランジションまとめ 優先度の低いステート更新をするためのAPI。 時間のかかるレンダリングの最中に 他のステート更新を受け付けたり、 サスペンドを起こしたレンダリングの最中に サスペンド前のUIを残し続けたり することができる。

Slide 24

Slide 24 text

宣伝 React 18までのトランジションについて、 細かな挙動まで含めて解説したZenn本

Slide 25

Slide 25 text

React 19の非同期トランジション startTransition(async () => { const result = await updateStuff(); setMessage( result.success ? “success!” : “ouch!” ); }); 非同期関数を渡せる (この非同期関数がアクション)

Slide 26

Slide 26 text

React 19の非同期トランジション startTransition(async () => { const result = await updateStuff(); setMessage( result.success ? “success!” : “ouch!” ); }); 非同期トランジションの実行中は isPendingがtrueになる。 完了したらfalseに戻る。 もちろん、トランジション内 でステート更新できる。

Slide 27

Slide 27 text

React 19の非同期トランジション 非同期処理の間isPendingがtrueになる点で、 非同期トランジションの挙動は、 普通のトランジション + Suspenseと同じ。 Q. どちらを使えばいいか?

Slide 28

Slide 28 text

React 19の非同期トランジション 非同期処理の間isPendingがtrueになる点で、 非同期トランジションの挙動は、 普通のトランジション + Suspenseと同じ。 Q. どちらを使えばいいか? A. 参照系はSuspense、更新系は非同期トランジション Suspenseは参照系向けのAPIで、更新系には向いて いなかった。非同期トランジションでそこが補完された。

Slide 29

Slide 29 text

React 19の非同期トランジション 公式ドキュメントにも Actions automatically manage submitting data for you という記載があり、 非同期トランジション(アクション)が更新系の サポートを意図したAPIであることが分かる。

Slide 30

Slide 30 text

アクションに関係する新しいAPI •useOptimistic • UIの楽観的更新を書きやすくするためのフック •useActionState • アクションの状態管理を楽に行うためのフック

Slide 31

Slide 31 text

useOptimistic

Slide 32

Slide 32 text

useOptimisticによる楽観的更新 楽観的更新とは、更新リクエストを送ったあと、 レスポンスが返ってくる前に更新後の状態を UIに反映させること。 例: Xで「いいね」を押すと、押した瞬間にUI上では いいね数が1増える(レスポンスが返ってくるのを待たない)

Slide 33

Slide 33 text

楽観的更新の面倒さ ステート管理が煩雑になりがち。 • エラー時に戻すことを考えると、元データを上書きできない • エラー時にステートを戻す処理を行いたいので ErrorBoundaryを活かせない

Slide 34

Slide 34 text

useOptimisticの使い方(宣言側) const [likes, setLikes] = useState(0); const [dispLikes, addOptimistic] = useOptimistic( likes, (currentLikes, optimisticValue) => { return currentLikes + optimisticValue; } );

Slide 35

Slide 35 text

useOptimisticの使い方(宣言側) const [likes, setLikes] = useState(0); const [dispLikes, addOptimistic] = useOptimistic( likes, (currentLikes, optimisticValue) => { return currentLikes + optimisticValue; } ); 元データの“楽観的更新版”のステートを宣言できる。 楽観的更新版 元データ

Slide 36

Slide 36 text

useOptimisticの使い方(更新側) const [dispLikes, addOptimistic] = useOptimistic(…); startTransition(async () => { addOptimistic(1); // 楽観的更新で1を足す const newLikes = await api.postLike(); setLikes(newLikes); // 元データを更新 });

Slide 37

Slide 37 text

useOptimisticの使い方(更新側) const [dispLikes, addOptimistic] = useOptimistic(…); startTransition(async () => { addOptimistic(1); // 楽観的更新で1を足す const newLikes = await api.postLike(); setLikes(newLikes); // 元データを更新 }); 非同期トランジションの中でaddOptimisticを呼び出す ことで、楽観的更新として反映される。

Slide 38

Slide 38 text

useOptimisticの動作例 const [likes, setLikes] = useState(0); const [dispLikes, addOptimistic] = useOptimistic( likes, (currentLikes, optimisticValue) => { return currentLikes + optimisticValue; } ); startTransition(async () => { addOptimistic(1); const newLikes = await api.postLike(); setLikes(newLikes); }); likes = 10 optimisticValue = 1 ① 非同期トランジション開始 → addOptimistic呼び出し dispLikes = 10 + 1 = 11 ⇒

Slide 39

Slide 39 text

useOptimisticの動作例 const [likes, setLikes] = useState(0); const [dispLikes, addOptimistic] = useOptimistic( likes, (currentLikes, optimisticValue) => { return currentLikes + optimisticValue; } ); startTransition(async () => { addOptimistic(1); const newLikes = await api.postLike(); setLikes(newLikes); }); likes = 11 ② ステート更新して トランジション終了 (楽観的更新状態ではなくなる) dispLikes = 11 ⇒

Slide 40

Slide 40 text

useOptimisticまとめ アクションの中で addOptimisticを呼び出すことで 楽観的更新を行う。 「アクションの実行中は楽観的更新の内容が表示される」 「アクションが完了したら自動的に元に戻る」 というシンプルな挙動であり、 アクションの概念を理解した人にとっては分かりやすい。

Slide 41

Slide 41 text

useOptimistic補足 Q. なぜ楽観的更新後の値を直接指定するのではなく関数を 介するのか? const [likes, setLikes] = useState(0); const [dispLikes, addOptimistic] = useOptimistic( likes, (currentLikes, optimisticValue) => { return currentLikes + optimisticValue; } );

Slide 42

Slide 42 text

useOptimistic補足 Q. なぜ楽観的更新後の値を直接指定するのではなく関数を 介するのか? A. 元の値が変化した場合や複数トランジションが走って いる場合にも対応するため

Slide 43

Slide 43 text

useActionState

Slide 44

Slide 44 text

useActionStateの概要 新しいステートを宣言するフック。 ステートの初期値と、 ステートを更新するためのアクションを受け取る。 ある意味useReducerに近い点もあるフック。

Slide 45

Slide 45 text

useActionStateの使い方(宣言側) const [state, runAction, isPending] = useActionState(async () => { const result = await api.postLike(); return result; }, null, ); ステートを保持 ステートの初期値 アクションの返り値でステート更新

Slide 46

Slide 46 text

useActionStateの使い方(呼び出し側) const [state, runAction, isPending] = useActionState(async () => { … }, null); … useActionStateから返ってきた関数を 呼び出せばアクションが実行される

Slide 47

Slide 47 text

useActionStateとトランジション const [state, runAction, isPending] = useActionState(async () => { … }, null); useActionStateから得られたrunActionを実行すると、 自動的に非同期トランジションになる。 (useActionStateにuseTransition相当の機能も 内包されている)

Slide 48

Slide 48 text

useActionStateまとめ useTransitionとuseStateをくっつけた 便利なフック。 あるいはuseReducerの非同期版。 これがあるので、非同期トランジションのために useTransitionを直接使うことは少ないかも。

Slide 49

Slide 49 text

useActionStateの追加機能 実は、連打したときにアクションが 複数並列実行されないように制御してくれる。 useReducerの非同期版と考えれば、並列実行 できないのは自然か。(Quramyさんの受け売り [1]) [1] Server Actions の同時実行制御と画面の状態更新 https://quramy.medium.com/server-actions- %E3%81%AE%E5%90%8C%E6%99%82%E5%AE%9F%E8%A1%8C%E5%88%B6%E5%BE%A1%E3%81%A8%E7% 94%BB%E9%9D%A2%E3%81%AE%E7%8A%B6%E6%85%8B%E6%9B%B4%E6%96%B0-35acf5d825ca

Slide 50

Slide 50 text

とアクション

Slide 51

Slide 51 text

の新機能 react-domの新機能として、の action属性にアクションを渡せるようになった。 フォームが送信されると呼び出される。

Slide 52

Slide 52 text

のactionにアクションを渡す { addOptimistic(1); const newLikes = await api.postLike(); setLikes(newLikes); }}> … アクションなので useOptimisticにも対応

Slide 53

Slide 53 text

のisPendingを取得したい アクションということはisPendingの概念があるはず。 それはuseFormStatusで取得できる。 const { isPending } = useFormStatus(); (非制御コンポーネントやサーバー関係の諸々のために FormDataを取得できる機能もある) の中のコンポーネント で呼び出す必要がある

Slide 54

Slide 54 text

useActionStateとの組み合わせ の外から状態を管理したい場合に有効。 const [state, runAction, isPending] = useActionState(async () => { … }, null); …

Slide 55

Slide 55 text

Server Actionsとの関係は?

Slide 56

Slide 56 text

Server ActionsとActionsの関係 Q. ここまで見てきたアクションと、Next.jsとかで出て くるServer Actionsの関係は?

Slide 57

Slide 57 text

Server ActionsとActionsの関係 Q. ここまで見てきたアクションと、Next.jsとかで出て くるServer Actionsの関係は? A. Server Actionsはクライアントから見たら非同期関数。 非同期関数はだいたいアクションである。 使う側から見れば、Server Actionsはアクションの一種。

Slide 58

Slide 58 text

Server Actionsに関係するAPI のactionやuseActionStateは、 Server Actionsと特に深い関係にある。 今回はサーバー周りまで踏み込む時間がないので 省略。

Slide 59

Slide 59 text

アクション系以外の 新機能ダイジェスト

Slide 60

Slide 60 text

メタデータ , , をレンダリング するとheadに反映してくれる。便利。

{post.title}

{post.title}

Slide 61

Slide 61 text

スタイルシート やも レンダリング可能。しかもSuspense対応。 (スタイルシートが読み込まれるまでサスペンドして コンポーネントの表示を待ってくれる) <div className=“card”> <link rel=“stylesheet” href=“…” precedence=“default” /> …

Slide 62

Slide 62 text

スクリプト もasync属性を付けることで サスペンド対応になる。謎のサードパーティ スクリプトを読み込む必要があるときに便利。 <div> <script async src=“nazo_gadget.js” /> <button onClick={() => NazoGadget.start()} /> </div>

Slide 63

Slide 63 text

Contextを直接コンポーネントとして 使えるやつ const MyContext = createContext(); // これまで // これから

Slide 64

Slide 64 text

ほか多数 他にも細かな改善がいろいろ入っていますが、 全部紹介する時間もないのでまた今度。

Slide 65

Slide 65 text

ほか多数 他にも細かな改善がいろいろ入っていますが、 全部紹介する時間もないのでまた今度。

Slide 66

Slide 66 text

No content

Slide 67

Slide 67 text

Zenn本「React 19新機能まるわかり」 このイベント終了後に配信開始 ¥0 (税込) アクション周りの話はもちろん、 今回紹介しきれなかった React 19の新機能を全部 詰め込んだZenn本が新登場。

Slide 68

Slide 68 text

React 19へのアップグレード

Slide 69

Slide 69 text

React 19の破壊的変更 メジャーバージョンアップなので破壊的変更がある。 内容的に、破壊力は控えめの印象。

Slide 70

Slide 70 text

古い機能の削除 すでに非推奨化済の古い機能がいろいろ削除される。 5年以上メンテされていないコードや外部ライブラリ がある人は要注意。 • 関数コンポーネントのdefaultProps • Legacy Context • String refs • など

Slide 71

Slide 71 text

TypeScript型定義の破壊的変更 こちらのほうが破壊力高いが、ランタイムの変更 ではないのでまだマシ。 const myRef = useRef(); React.MutableRef const [state, dispatch] = useReducer(); 引数の無いuseRefは不可になった MutableRefという型はAPIで 使われなくなり、非推奨になった useReducerの型引数は 破壊的変更が入った

Slide 72

Slide 72 text

まとめ

Slide 73

Slide 73 text

まとめ React 19で新しく登場したAPIたちは、 アクションという新概念でまとめられている。 (今回省略したuseはまた別) Suspenseやトランジションという既存概念を 土台にアクションを理解することが React 19を使いこなす近道となる。