ブログトップ

公開日:

更新日:

21 min read

技術革新

実装経験から考えるReactの選択:AIとの対話を通じて

実装経験から考えるReactの選択:AIとの対話を通じてのイメージ

JavaScriptに興味を持つ世界中のIT技術者2万3540人が回答したアンケートによれば、フロントエンドフレームワークの利用率ランキングはReactが8年連続1位で、しかも利用率を伸ばし続けています。

この圧倒的な人気は、実際の開発現場でも顕著です。「チームが慣れている」「得意である」という理由だけで、技術的な検討が不十分なままReactが採用されるケースを、私はよく目にしてきました。例えば:

  • サーバーの長時間処理をモニタリングするページ
  • リアルタイムストリーミングを扱うアプリケーション

これらは本当にReactが最適な選択なのでしょうか?

技術選定への疑問: 偶然の発見から始まった対話

私も次のプロジェクトでNext.jsを使おうと考えていました。VanillaJS、jQuery、Alpine.jsの経験はありますが、Reactは未経験です。

そんな時、ネットで興味深い投稿を見つけました。「reactのつらいところ」という質問への回答です:

Reactは、単純な依存関係を持つUIの構築に適していますが、時間的な依存関係や循環的な依存関係が発生する場合には、フレームワークとの相性が悪くなることがあります。

例として、ビデオチャットアプリでカメラ一覧を表示する際、非同期APIの呼び出しと再描画のサイクルが発生し、実装が複雑になってしまう問題が挙げられます。このような場合、フレームワークを使用しない方が適している可能性があります。

この指摘が気になり、AIに見解を求めてみることにしました。結果として、この対話は「本当にReactが最適な選択なのか」という本質的な問いへと発展していきました。

Reactの非同期処理における課題

ご指摘の問題についての理解は正確で、特に以下の点について補足させていただきます:

ReactではuseEffectと空の依存配列を使用することで、初回レンダリング時のみカメラ一覧を取得するという技術的な解決策は存在します。しかし、これは表面的な解決策に過ぎず、「時間的な依存関係を持つ処理をリアクティブなパラダイムに無理やり押し込めている」という本質的な問題は残ったままです。

特に、以下のような要件が加わると状況は更に複雑になります:

  • カメラの接続/切断の検知と一覧の更新
  • ユーザーの許可状態に応じた表示の変更
  • 個々のカメラの状態(使用中かどうか)の表示

このような状況に対しては、別のアプローチを検討する価値があります:

  1. より命令的なアプローチ(イベントベースの設計)の採用
  2. 状態管理をReactの外に移す
  3. より低レベルなアプローチ(WebRTC APIを直接使用)

結論として、すべての問題をリアクティブなパラダイムで解決しようとするのではなく、問題の性質に応じて適切なアプローチを選択することが重要です。

Reactの非同期処理における課題

リアクティブって何ですか???

リアクティブとは「データの変更に反応して自動的にUIを更新する」というアプローチです。 例えば、ユーザーリストのデータが更新されると、それを表示しているUIが自動的に再描画される。 このパラダイムは多くのケースでとても便利です。

しかし、カメラの制御のような「継続的に変化する状態」の場合、この「変更→再描画」という パターンが逆に問題を引き起こします。なぜなら、カメラの状態は連続的に変化し、 その都度UIを再構築するのは非効率だからです。

ReactではuseEffectと空の依存配列を使用することで、初回レンダリング時のみカメラ一覧を取得するという 技術的な解決策は存在します。しかし、これは表面的な解決策に過ぎず、 「時間的な依存関係を持つ処理をリアクティブなパラダイムに無理やり押し込めている」という 本質的な問題は残ったままです。

なるほど。私はさらに疑問をAIにぶつけました。

同じようなプログラムをjqueryで作ったことがありますが、そんな複雑なことはありませんでした。 この程度の非同期なんてザラにあるのに、それを考慮していないreactはおバカな仕様に思えますが、 next.jsでは解決されてるのでしょうか

AIは次のように答えました。

Next.jsは確かにReactの非同期処理に関する問題に対して、より良いソリューションを提供していますが、完全な解決とは言えない部分もあります。

Next.jsでの主なアプローチは:

  • Server Componentsによるサーバーサイドでのデータフェッチ
  • useEffectの代わりにuse clientディレクティブと組み合わせたuseCallbackuseStateの利用

しかし、おっしゃる通り、jQueryの方がシンプルに書けるケースは確かにあります。

Reactの問題点は:

  1. 宣言的UIと副作用の分離を強制するあまり、シンプルな非同期処理まで複雑化してしまう
  2. コンポーネントのライフサイクルと非同期処理の相性が悪い
  3. 状態管理と副作用の分離が時として過剰な抽象化を招く

この状況に対する個人的な見解:

  1. Reactは大規模アプリケーションの一貫性を保つために、あえて制約を設けている面がある
  2. しかし、単純な非同期処理に対してまで同じアプローチを強制するのは過剰
  3. jQueryの方が適している場合は、実際そちらを選択するのも賢明

より現代的なアプローチとしては、Svelteのような、よりシンプルな非同期処理モデルを持つフレームワークを検討する価値もあります。

結論として、ReactやNext.jsは確かに強力なツールですが、すべてのケースで最適とは限りません。シンプルな非同期処理に関しては、より直接的なアプローチ(jQuery的な)や、より自然な非同期処理モデルを持つフレームワークを選択する方が合理的な場合も多いと考えられます。

そうなんだ。知りたいことを要約して回答してくれるのAIの強みだ。 自分で本を読めば、該当箇所にたどり着くのが大変だ。しかも記述は分散している。 ネットで検索すると、的を射ていない記事を大量に読まされる。

大規模プロジェクトでの非同期処理

私は質問を続けた。

大規模プロジェクトではバックエンドとの同期がメインで非同期はあまり発生しないからですか?

大規模プロジェクトではより多くの非同期処理が発生します:

  1. バックエンドとの通信自体が非同期
  • REST API呼び出し
  • WebSocketによるリアルタイム通信
  • GraphQLのクエリ/ミューテーション
  • Server-Sent Events
  1. ブラウザAPIとの連携
  • カメラ、マイク、位置情報などのデバイスAPI
  • ファイルシステムAPI
  • IndexedDBなどのストレージAPI
  • プッシュ通知
  1. 複雑な状態管理
  • 複数の非同期処理の結果を組み合わせる
  • キャッシュの管理
  • 楽観的更新(Optimistic Updates)
  • エラーハンドリングとリトライロジック

このような複雑な非同期処理を扱う際の主な課題:

  1. 依存関係の管理
  • どの処理がどの状態に依存しているか
  • 処理の順序性の保証
  • エラー時の状態の一貫性維持
  1. メモリリーク防止
  • コンポーネントのアンマウント時のクリーンアップ
  • 購読解除の適切な実行
  1. パフォーマンス最適化
  • 不要な再レンダリングの防止
  • キャッシュの効率的な利用

これらの課題に対して、React Query / SWR、Redux Saga、Next.js (Server Components)など、現代のフレームワークやライブラリは異なるアプローチを提供しています。

結論として:

  • 大規模プロジェクトではむしろ非同期処理が増える
  • その複雑さを管理するためのパターンやツールが必要
  • Reactの制約は、この複雑さに対する一つの解決アプローチ
  • ただし、シンプルなケースまで複雑化させてしまう副作用がある

このトレードオフを理解した上で、プロジェクトの規模や要件に応じて適切なアプローチを選択することが重要です。

Reactが想定する非同期パターン

うーん、私の質問の仕方が悪かったかな…まあいいか。つまり、 想定している非同期の種類が限定されているということか。

Reactが想定している非同期パターンは、主に以下のようなものに最適化されています:

  1. データフェッチのパターン
  • 初期データの読み込みと表示
  • ローディング状態の管理
  1. サーバーとの同期的な状態更新
  • データの作成・更新・削除
  • 状態の同期管理

一方で、以下のような非同期パターンはReactと相性が悪いです:

  1. ストリーミングデータの継続的な処理
  • 動画再生時間の更新
  • センサーデータの継続的な取得
  1. 双方向の非同期通信
  • WebRTCを使用したビデオ通話
  • リアルタイムな双方向通信
  • 接続状態の管理
  1. ハードウェアとの連携
  • シリアルポートの制御
  • 継続的なデータ読み取り
  • デバイスの状態管理

Reactの制約が有効なケース:

  • サーバーからのデータフェッチ
  • フォーム送信
  • APIコール
  • キャッシュ管理

Reactの制約が過剰なケース:

  • ストリーミングデータ処理
  • リアルタイム通信
  • ハードウェア制御
  • 双方向通信
  • 継続的なイベント処理

このように、Reactは特定の非同期パターン(主にHTTP通信とそれに関連する状態管理)に最適化されており、それ以外のパターンに対しては、必ずしもベストな選択ではないかもしれません。

実践例:ライブストリーミングアプリケーション

それそれ、「サーバーとの同期的な状態更新」を言いたかったのですよ。 言われてみれば、確かに私が作ったのはライブストリーミングのアプリだったなあ。 ライブコマースで2つのカメラと演出用動画のストリーミングにテキストチャットを1つに合成して配信するアプリです。

ライブストリーミングアプリやカメラ制御は、Reactの宣言的な状態管理モデルと相性が悪いユースケースの典型例です。

以下のような特徴が、Reactのモデルと衝突します:

  1. 継続的なメディアストリームの処理
  • メディアストリームの取得と管理
  • ビデオ要素への接続
  • ストリームのライフサイクル管理
  1. リアルタイムのメディア制御
  • 音声・映像のミュート制御
  • トラックの有効/無効切り替え
  • 状態の即時反映
  1. WebRTCピア接続の管理
  • 接続状態の管理
  • リモートストリームの制御
  • シグナリング処理

このような場合、より適切なアプローチとして:

  1. メディア処理のロジックをReactの外に移す
  2. 状態管理をカスタムフックに分離
  3. WebRTCロジックの分離

一方、jQueryでの実装は非常にストレートです。特にメディアストリームのような「継続的に存在する状態」は、Reactの「レンダリングのたびに状態を再構築する」というモデルと相性が悪く、jQueryのような素直なアプローチの方が適している場合があります。

Reactで同様の実装を行う場合、以下のような「余計な」考慮が必要になります:

  1. デバイス一覧の状態管理
  2. ストリームの状態管理
  3. コンポーネントのライフサイクル管理
  4. 副作用の制御
  5. クリーンアップ処理

結論として、このようなユースケースでは、必ずしもReactが最適な選択とは限らず、より直接的なアプローチを選択することも検討する価値があります。

ゲーム開発から見えるReactの特性

つまり、reactでゲームを作るのも大変なのか。

ゲーム開発もReactのパラダイムと相性が悪い典型的な例です。

ゲーム開発で必要な要素:

  1. ゲームループ(継続的な状態更新)
  • フレームごとの状態更新
  • アニメーションの管理
  • パフォーマンスの最適化
  1. キーボード/マウス入力の処理
  • リアルタイムな入力検出
  • 複数キーの同時押し対応
  • イベントハンドリング
  1. 物理演算やコリジョン検出
  • オブジェクト間の衝突検出
  • リアルタイムな物理計算
  • 状態の即時更新

より良いアプローチ:

  1. Canvas/WebGLを直接操作
  • 低レベルな描画制御
  • パフォーマンスの最適化
  • Reactは最小限のラッパーとして使用
  1. ゲームエンジンの使用
  • Three.jsやPhaserなどの専用エンジン活用
  • 既存のゲーム開発ツールの活用
  1. ゲームロジックとUIの分離
  • ゲームロジックは純粋なJavaScriptで実装
  • Reactはスコア表示などのUI要素に限定

結論として:

  • ゲーム開発では、フレームごとの継続的な状態更新が必要
  • リアルタイムの入力処理や物理演算が重要
  • これらはReactの「状態変更→再レンダリング」モデルと相性が悪い
  • ゲーム部分は従来の命令型プログラミングで実装し、必要に応じてReactでUIを提供する

実際、多くのWebゲームはCanvas/WebGL上で直接描画を行い、Reactは周辺のUI(メニュー、スコアボード、設定画面など)の管理にのみ使用する形を取っています。

ストリーミングでもcanvasに直接描画したなあ。 昔のWindowsみたいなプログラムを書きましたよ。

なるほど!まさにその通りですね。キャンバスに直接描画していく手法は、メディアストリーミングのような継続的な描画が必要なケースで非常に効果的です。

このアプローチの利点:

  1. パフォーマンスが良い(DOMの更新が最小限)
  2. 直接的なメディア操作が可能
  3. 描画の細かい制御が可能(フィルターやエフェクトの追加など)
  4. 状態管理がシンプル

まさに「昔のWindows」的なアプローチですよね。イベントドリブンで、直接的なグラフィックス操作という、懐かしくも実用的なプログラミングパラダイムです!

まとめてみた

つまり、リアクティブはデータドリブンに特化していて、イベントドリブンには向いていないということなんだな。

Reactの得意分野(データドリブン):

  • データの取得と表示
  • フォーム送信・APIコール
  • サーバーとの同期的な状態更新
  • UIコンポーネントの状態管理
  • キャッシュ管理

Reactが不得意な領域(イベントドリブン):

  • メディアストリームの制御
  • デバイスAPI(カメラ、マイクなど)
  • リアルタイムな双方向通信
  • 継続的なデータ更新
  • ゲームループ
  • キャンバス描画

よくわかった!(気がする)

対話を通じた気づき

この一連の対話を通じて、私は重要な気づきを得ることができました。 それは、Reactには明確な得意・不得意分野があるということ、 そしてプロジェクトの性質に応じて適切な技術を選ぶことの重要性です。

さらに興味深いのは、この気づきがAIとの対話を通じて得られたということです。 一方的な説明ではなく、私の経験や疑問に基づいた双方向の対話により、より深い理解に至ることができました。 AIは私の具体的な経験を踏まえながら、より広い文脈で問題を説明してくれました。

この経験は、AIとの対話が技術的な理解を深める有効な手段になり得ることを示しています。 特に、「一般的な認識」や「トレンド」に疑問を持ったとき、AIとの対話を通じて新しい視点を得られる可能性があります。

ただし、重要なのは、AIとの対話が一方的な質問と回答ではなく、 自分の経験や具体的な事例を交えながら進めることです。そうすることで、 より実践的で深い理解につながるのではないでしょうか。