Open137

React Conf 2024 全部観る

Shinya FujinoShinya Fujino

気温も段々と涼しくなり、Next.js 15 も正式に発表されたので、React の知識をアップデートしていく機運が高まってきた。ここでは今年の 5 月に開催された React Conf 2024 の動画をすべて視聴し、その感想を記していく(React Native については疎いため雑に流す予定)。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

まずは Joe Savona から React の目的について語られる。こういうのを改めてちゃんと言語化するのは良いこと:

Make it easy for anyone to build great user experiences.

UX については以下のように説明される:

  • User Experience (UX)
    • Fast startup
    • Responsive interactions
    • Delightful extras and polish

そして easy for anyone の部分は以下のように説明される:

  • Developer Experience (DX)
    • Low barrier to entry
    • High productivity
    • Ability to scale
Shinya FujinoShinya Fujino

なお Joe Savona は 2010 年頃に日本で開発者としてのキャリアを開始し、そこで生の HTML や CSS、jQuery などを書いて IE6 と戦っていたというプチ情報も開示された。

Shinya FujinoShinya Fujino

2023 年全体で、React は合計 1,025,630,896 回 npm からダウンロードされた。

Shinya FujinoShinya Fujino

React の成功に伴いあらゆるライブラリが用意されるようになったが、選択肢が多いことはユーザーに選択を強いることでもある。ので、ユーザーが迷わないように、React は以下のフレームワークをおすすめしている:

  • Remix
  • RedwoodJS
  • Next.js
  • Expo

https://react.dev/learn/start-a-new-react-project の内容と比較すると、Gatsby が姿を消しているのが興味深い。

Shinya FujinoShinya Fujino

https://react.dev/blog/2024/04/25/react-19#support-for-metadata-tags 以下にあるメタデータの hoisting の話。メタデータを確定させるコンポーネントにそのままタグを配置できるようになるという意味で composability が上がるよねという話で、確かにそうだと思う。ただ、実際にはこういう書き方はせずフレームワークに委ねることになるかな。

Shinya FujinoShinya Fujino

続いてスタイルの composability の話で、https://react.dev/blog/2024/04/25/react-19#support-for-stylesheets に対応する。これもスタイルシートを必要としているコンポーネントとそれをロードするタグが同居できたほうが良いよね、という内容。CSS の場合は読み込み順序が重要だが、それは precedence で指定できる。FOUC を回避したり、duplication を取り除いたりといったお世話を React がしてくれる。

Shinya FujinoShinya Fujino

Hydration に関するエラーが diff 付きで見やすくなるらしい、良き:

Shinya FujinoShinya Fujino

続いて Actions の話。フレームワーク無しで使ってクライアントサイドでも利用できるというのは、普段データの mutation にしか使っていなかったので抜けていた視点だ。

Actions are not exclusive to Server Components frameworks - they also work on the client, even without a framework.

Shinya FujinoShinya Fujino

Remix からの影響への言及:

Remix inspired a resurgence of action-based APIs in JS frameworks

Shinya FujinoShinya Fujino

既存の HTML action と JS のイベントハンドラーに関する pros と cons が語られる:

  • HTML action
    • pros
      • Simple "reload everything" model
      • Works without JS
    • cons
      • Limited feedback to user input
      • Hard to add client behavior
  • Event handler
    • pros
      • Instant feedback to user input
      • Mix client and server behavior
    • cons
      • Async data management is hard
      • Not interactive until JS loads
Shinya FujinoShinya Fujino
<form action={checkoutAction} />

<form onSubmit={onCheckout} />

と表面的には違いがないが、決定的な違いとして

  • Interactive before hydration
  • Built-in support for essential UX patterns like optimistic updates, with just a few lines of code

あたりが言及されている。

Shinya FujinoShinya Fujino

続いてパフォーマンスの話、コンパイラ関連の話題が続くと思われる。

Shinya FujinoShinya Fujino

どうでもいいが、Sathya 氏は yarn を使っていて、Meta ではパッケージマネージャーはやはり yarn で統一されているのだろうか。

Shinya FujinoShinya Fujino

Compiler の定義:

A compiler is a program that translates code from a source language to a traget language

Shinya FujinoShinya Fujino

Fine-Grained Reactivity が DX を損なわずに達成できる、といういい話。How React compiler sees your code の図は誰かに説明するときに使えそう:

Shinya FujinoShinya Fujino

続いては Meta での実例の話。まず、既存コードを並べ、どれだけ momeization のために費やしており、readability が低下しているかをビジュアルに表現している(赤と白の部分がコアロジックと関係ない部分)、つらい:

Shinya FujinoShinya Fujino

Meta ではこういうつらみが React Compiler により既に取り除かれていますよという例として、Instagram と Quest Store の話が出てくる。

Shinya FujinoShinya Fujino

コードから認知負荷が取り除かれるだけでなく、パフォーマンスも向上した:

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

Remix が Shopify に吸収されて、理想と現実をバランスよく取り込めていることが語られる。

Shinya FujinoShinya Fujino

なるほど、CRA などで作られた React Router を使っているアプリをモダンにするためには、

React Router + Vite Plugin = Remix

という等式(?)から、Remix に移行してもらえばよいと考えていたが、多くの人たちにとってこれは rewrite に感じられ、規模の大きなアプリにとっては厳しくなる。そこで、

React Router + Vite Plugin = React Router v7

と Remix を React Router に置き換えてしまえば、作業内容はフレームワークの導入ではなくパッケージのメジャーバージョン更新となり、心理的障壁が減る。だから Remix に誘導するのではなく React Router の方に寄せていく、と。そうすればレガシーなアプリに

  • Automatic code splitting
  • Simplified data loading
  • Form Actions, Server actions
  • Simplified pending states
  • Optimistic UI
  • Server rendering
  • Static pre-rendering
  • RSC

等の恩恵をもたらすことができる。

Shinya FujinoShinya Fujino

Remix が React Router になる、というざっくりした話だけを聞いたときは「どうして?」と思ったし、npm のダウンロード数的に React Router が圧倒的に多いのでそこに乗っかりたいのかなとか邪推したけど、「既存のレガシーアプリを救いたい」みたいな話なのであれば一応腑に落ちるかも。

Shinya FujinoShinya Fujino

マイグレーションのデモでは clientLoader を使って useState を削除していっており気持ちいい。

Shinya FujinoShinya Fujino

SSR や RSC への移行についてもデモで示されている。RSC の方は JSX を loader に移動するだけで動いており素晴らしい。

Shinya FujinoShinya Fujino

実際、いくら React がフレームワークを使えと言っても、シンプルな SPA とフル機能の Next.js の中間に落とし込みたいシーンはたくさんあるはずなので、それらの間を incremental に行き来できるようにしたいというモチベーションは理解できるなあ。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

フォーム関連は Next.js と絡めてどう使うべきなのか自分の中でもしっかり整理できていないので、コードを書いてベストプラクティスを探っていかないといけない。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

デモを通じて新しい API のありがたさを実感しよう、的な流れの模様。

Shinya FujinoShinya Fujino

startTransition により複数の非同期処理呼び出しの coordination ができるよという話。

Shinya FujinoShinya Fujino

内部での state 更新のタイミングが即時反映されないよという話。

Shinya FujinoShinya Fujino

イベントハンドラーをクライアントサイドのアクションに変更して startTransition を削除して、同様の coordination が達成される話。

Shinya FujinoShinya Fujino

startTransition を削除し isPending が使えなくなったので、代わりに useFormStatus を使う。

Shinya FujinoShinya Fujino

React 19 in Action という感じで、これまでまとめ的に紹介されてきた機能の一部をデモする感じだった。上に書いた内容の具体的な使い方を見られるのでコードをまず確認したい人はこれから見るのが良さそう。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

「コンパイラ、本当に動くの?」という疑問に答えるために、

  1. Adding the compiler to your app
  2. Writing new code
  3. Building intuition

の流れでデモをする。

Shinya FujinoShinya Fujino

まずは babel-plugin-react-compiler@beta をインストールして Babel のセットアップ:

Shinya FujinoShinya Fujino

コンパイラが有効化されている場合、React Developer Tools 上でコンパイラにより最適化されたことを示す Memo というマークが表示される(左がコンパイラ有りのアプリ、右はなし):

Shinya FujinoShinya Fujino

コンパイラの説明をするために、変数の依存関係を可視化したりする VS Code 拡張を自作したらしい、素晴らしい:

Shinya FujinoShinya Fujino

useMemo の依存をコンパイラがより正確に検知してくれることが示される。

Shinya FujinoShinya Fujino

続いて Writing new code のお題として dislike button を追加するらしい、面白い。

Shinya FujinoShinya Fujino

Rules of React を破った際に eslint-plugin-react-compiler が警告してくれるということと、こういうコードはコンパイラがスキップしてくれるという話(また、スキップされた箇所以外はちゃんと最適化される):

Shinya FujinoShinya Fujino
  • What does React Compiler Optimize?
    • Skipping re-renders
    • Skipping expensive calculations
Shinya FujinoShinya Fujino

めちゃくちゃ説明が上手いし話も面白いし、Overview を伝えるために細かいことを飛ばす塩梅がよかった。

なお、同じことをやってみようかと Bluesky のコードを見てみたらすでに React Compiler が導入されていた:

https://github.com/bluesky-social/social-app/blob/e9f6a8e58d69ea3a210da895f1159c682467ecff/package.json#L244

https://github.com/bluesky-social/social-app/blob/e9f6a8e58d69ea3a210da895f1159c682467ecff/babel.config.js#L20

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

「過去の自分が書いたスクリプトをもとに、今の自分が舞台上で話している」という何気ない構造が上手く RSC の話につながるようになっていて、これはさすがに惚れる:

Shinya FujinoShinya Fujino

クライアントで fetch していたデータをサーバーサイドで取得するようにして、それをシリアライズしてクライアントに渡すようにする、という話をまず React について一切触れず誰でも理解できるプレーンな JS の範囲でデモしておりイケメンすぎる。エッセンスをできる限りわかりやすい言葉に置き換えて説明する技術が Dan Abramov は異常に優れていて、このトークにも現れている。

Shinya FujinoShinya Fujino

この「複数の環境をまたいだ多段階計算を React ならもっと上手くできるよ」という感じでコンポーネントを導入していく。

Shinya FujinoShinya Fujino

最後は「準備していたスクリプトをもとにこのトークをやり遂げたので、予定していた多段階計算は完了しました」みたいな感じで最初の話につなげてオシャレに終わる。Dan Abramov のように話せるエンジニアはやはり稀有だと思う。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

Universal RSC により、ウェブと iOS と Android すべてのプラットフォーム向けに RSC の機能を提供するらしい。やり取りしているデータやサーバーサイドはどうなっているのか全然想像できないがすごい。

Shinya FujinoShinya Fujino

やり取りしているデータは RSC ペイロードっぽい?色々な UI がブリブリ動いていてすごかった。今度 Expo 触ってみたいなあ。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

時差ぼけしていてしかも AirPods をなくしてしまい微妙に凹んでいるっぽいw

Shinya FujinoShinya Fujino

サーバーレス環境で

let counter = 0;

function onRequest(request: Request) {
  if (request.method === "GET") {
    return new Response(counter);
  } else if (request.method === "POST") {
    counter++;
    return new Response(counter);
  }
}

のようなコードを書いても、毎回環境がリセットされるのでカウンターが機能しない。これは言語の直感に合わないよね、と。

Shinya FujinoShinya Fujino

この問題を解決するには外部にストレージを用意する必要があるが、管理の手間が増えるし、この問題を解決するための方法としては大げさすぎる。

Shinya FujinoShinya Fujino
class Counter {
  value = 0;
  onRequest(request: Request) {
    if (request.method === "GET") {
      return new Response(this.value);
    } else if (request.method === "POST") {
      this.value++;
      return new Response(this.value);
    }
  }
}

のように書けるとしたら良さそうだが、Cloudflare の Durable Objects がまさにそれだという話。

Shinya FujinoShinya Fujino

で、RSC を statefull にしたい、さらにその state を Server Action からも扱えるようにしたい、みたいな話につながっていくのか。

Shinya FujinoShinya Fujino

こういう問題意識をもとに、react-party というライブラリを作っていて、そこでは上の Durable Objects のコードと同じように

import {
  useState,
  useReducer,
  createContext,
  useContext,
  ReactServer
} from "react-party";

export class MyServer extends ReactServer {
  render() {
    return <App />;
  }
}

のようにして書けると、なるほど。

Shinya FujinoShinya Fujino

既存のフレームワークと組み合わせて使えるようになる予定らしい:

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

React が交通整理をやってくれると:

React coordinates so your application can compose

Shinya FujinoShinya Fujino

具体的には以下のような感じで title や link を colocation できるようになる:

Shinya FujinoShinya Fujino

ただ、スタイルとかはまだわかるものの、タイトルってコンポーネントの外部世界の話で、積極的に colocate すべきものではない気がする。あくまでコンポーネントツリーの末端でしかタイトルを確定できない場合のエスケープハッチ的に使ったほうがいいかも。

Shinya FujinoShinya Fujino

colocation という言葉に引っ張られたけど、React Helmet とか外部のライブラリがユーザーランドで無理矢理頑張っていた感は確かにあるので、React 側で吸収してくれるのは普通に嬉しい。

Shinya FujinoShinya Fujino

ボットやクローラーなどのツールにとっては head 内に spec に沿って適切にデータがあると嬉しいが、UX のために即時にストリームでレスポンスを返したいという気持ちとぶつかる。React は両者をバランスさせ、なるべく spec に沿って要素を head に配置することを試みるが、ストリーム中に見つかったものについては body 内に配置する、という話。

Shinya FujinoShinya Fujino

async script による実行順序の reodering の話(https://react.dev/reference/react-dom/components/script )、リソースのプリロードの話(https://react.dev/reference/react-dom#resource-preloading-apis )、フォントや画像、スタイルシートなどを優先度高く取得する話。ここらへんのパフォーマンスの話は自分の理解が曖昧なので、

とかを一度読んでからもう一度ドキュメントを確認したほうが良さそう。

Shinya FujinoShinya Fujino

<link> や <style> をコンポーネント内に置ける話。precedence で読み込みの順序を指示できたり、UI がストリームされる場合でもいい感じに FOUC を排除してくれたり、Suspense 境界で必要なスタイルシートがロードされることを保証してくれたり、といった話題が続く。

Shinya FujinoShinya Fujino

色々書いたけど、このあたりを全体的に React がお世話してくれるということらしい。このあたりは完全に理解して記事とかにまとめたいなあ。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

以下のルールを破ってみると:

  • Only call Hooks at the top level
  • Only use Hooks from React functions
Shinya FujinoShinya Fujino

if 文の中に useState を置くと何が起こるかという話。useState を呼ぶと linked list が作られ、各ノードで状態が管理される。初回のレンダリングでは if 文の中は実行されないため quantity に対応するノードが作成されない。そしてボタンを押して ingredient に値がセットされると、if 文の中の useState が実行されるが、これは 2 番目の呼び出しであるため、needsPurchase の値を参照してしまう。そして最後の useState の箇所で参照先のノードがないためエラーが発生する:

Shinya FujinoShinya Fujino

通常の関数としてコンポーネントを呼び出してはいけないという話。renderIngredient は FlatList の中で ingredients にマップされるとして、通常の関数呼び出しとなるため状態が FlatList のものと認識される。ここで addIngredient により ingredients の中身が追加されると、上の話と同様に 3 回目の useState で存在しないノードを参照してエラーとなってしまう:

Shinya FujinoShinya Fujino

最後に React 内部のアルゴリズムに興味がある人向けに参考文献が示されていた。Build your own React は自分も昔読んだが素晴らしい記事だったのを覚えている。

https://pomb.us/build-your-own-react/
https://www.youtube.com/watch?v=KJP1E-Y-xyo
https://www.zhenghao.io/posts/react-rerender
https://blog.isquaredsoftware.com/2020/05/blogged-answers-a-mostly-complete-guide-to-react-rendering-behavior/
https://gitnation.com/contents/understanding-reacts-fiber-architecture

Shinya FujinoShinya Fujino

これらの実装の詳細は変化し得るためそれほど重要ではなく、ルールに従った良いコードを書くことが重要ですよ、というのも最後に触れていた。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

この 20 年でアプリケーション開発は大きく姿を変えたが、データを CRUD するという核心は変わっていないという話。

Shinya FujinoShinya Fujino

RedwoodJS が他のフレームワークと一線を画す点として、DB が同梱されている点だとする。このあたりは最初の話とつながる。データレイヤーは以前は GraphQL と結合していたが今はそうではないらしい。

Shinya FujinoShinya Fujino

yarn rw g cell Sample みたいなコマンドにより cell というモデル(?)のようなものを生成できるらしい。めっちゃ Rails っぽい。

Shinya FujinoShinya Fujino

cell 生成コマンドにより、下記のようなデータを取得するためのクエリと、データの Loading、Empty、Failure、Success に対応するコンポーネントが生成されるらしい。なるほど全然想像と違ったけど結構面白いかも 👀

export const QUERY = gql`
  query {
    posts {
      id
      title
      body
      createdAt
    }
  }
`

export const Loading = () => <div>Loading...</div>

export const Empty = () => <div>No posts yet!</div>

export const Failure = ({ error }) => (
  <div>Error loading posts: {error.message}</div>
)

export const Success = ({ posts }) => {
  return posts.map((post) => (
    <article>
      <h2>{post.title}</h2>
      <div>{post.body}</div>
    </article>
  ))
}
Shinya FujinoShinya Fujino

RSC 対応版は QUERY が下記のようになるらしい:

export const data = () => {
  return { products: db.products.findMany() }
}
Shinya FujinoShinya Fujino

File-Based Routing じゃなくて、Router 用のファイルに全部記述していくスタイルらしい。どこに Auth が入るかとか 1 ファイルで見渡せていいかも。

Shinya FujinoShinya Fujino

Starter Kits とかとの違いとして、それらは一度プロジェクトを scaffold したら依存の管理とかはこちらの責務だが、RedwoodJS なら yarn rw upgrade でできると。まあそうかもしれないが、これはこれで実際はつらみがありそうだけど。

Shinya FujinoShinya Fujino

まったく事前情報がなかったので新鮮で面白かった。仕事で使うことは当分ないと思うけど、個人プロジェクトとかなら少し触ってみたいかも。

Shinya FujinoShinya Fujino
Shinya FujinoShinya Fujino

司会は chantastic こと Michael Chan。Meta と Vercel の人たち(Dan Abramov は Bluesky だけど)への質問コーナーでめちゃくちゃ楽しみ。

Shinya FujinoShinya Fujino

最初の 20 分くらいは React Compiler の話。現在は Memoization 関連に焦点を当てているがもっと様々な方向性に活かされる予定ということや、Babel プラグインは単に既存アプリケーションへのバインディングの端緒という位置付けで、SWC など他のツールのサポートも追加予定であること、などなど。