React 19.2の<Activity>はVue.jsのv-showと同じ用途か?

こんにちは。エンジニアの橋本です。
React 19.2がリリースされ、新機能も追加されましたね。
その中でも、XでVue.jsのv-showのようで既視感があると言われていた<Activity>が気になったので、調べてみました。

Reactの<Activity>

今までのReactでは要素の表示・非表示を切り替える場合、条件付きレンダリングを使うことが多かったです。 Vue.jsのv-ifと同じように、条件がFalsyの場合、コンポーネントは破棄され、要素はDOMから完全に削除されます。

{isShowingSidebar && (
  <Sidebar />
)}

そしてReact 19.2からは<Activity>を使うことで、要素の表示・非表示を切り替えることができます。こちらはVue.jsのv-showと同じように、CSSのdisplayプロパティを切り替えることで要素の表示・非表示を切り替えます。つまりコンポーネントは破棄されず、要素は常にDOMに存在します。

<Activity mode={isShowingSidebar ? 'visible' : 'hidden'}>
  <Sidebar />
</Activity>

Vue.jsのv-show

Vue.jsでは、表示・非表示を切り替えるときにv-ifv-showの2つの方法があります。 v-ifは条件付きレンダリングで、要素の表示・非表示を切り替えます。条件がFalsyの場合、コンポーネントは破棄され、要素はDOMから完全に削除されます。

<Sidebar v-if="isShowingSidebar" />

一方、v-showはCSSのdisplayプロパティを切り替えることで要素の表示・非表示を切り替えます。 コンポーネントは破棄されず、要素は常にDOMに存在します。

<Sidebar v-show="isShowingSidebar" />

Reactの<Activity>とVue.jsのv-showの違い

次にレンダリングして比較してみましょう。

Vue.jsのv-showの場合

Vue.jsのv-showの場合

Reactの<Activity>の場合

Reactの<Activity />の場合

一見すると同じように見えますね。displayプロパティで切り替えているだけであれば<Activity>を使わずとも以下でも良さそうです。
※Sidebarのpropsにstyleを用意する必要がありますが、あくまで例示のため省略しています

<Sidebar style={{ display: isShowingSidebar ? undefined : 'none' }} />

より詳細な情報を知るために公式サイトを見てみましょう。
Reactの公式サイトには以下のように書かれています。

When an Activity boundary is hidden, React will visually hide its children using the display: "none" CSS property. It will also destroy their Effects, cleaning up any active subscriptions.

While hidden, children still re-render in response to new props, albeit at a lower priority than the rest of the content.

When the boundary becomes visible again, React will reveal the children with their previous state restored, and re-create their Effects.

引用元:<Activity> – React

<Activity>で非表示にしている間も子コンポーネントは新しいpropsに応じて再レンダリングされますが、他のコンテンツよりも優先度が低くなるそうです。また、再度表示されるときには、子コンポーネントのStateが復元され、Effectsも再作成されるそうです。

Thus, in addition to hiding and showing content, Activity boundaries help improve your app’s performance during hydration by letting React know which parts of your page can become interactive in isolation.

And even if your page doesn’t ever hide part of its content, you can still add always-visible Activity boundaries to improve hydration performance:

function Page() {
  return (
    <>
      <Post />

      <Activity>
        <Comments />
      </Activity>
    </>
  );
}

引用元:<Activity> – React

さらに、<Activity>は、ページのどの部分が独立してインタラクティブになり得るかをReactに知らせることで、ハイドレーション時のアプリのパフォーマンス向上に貢献するそうです。また非表示にしない要素の場合でも、常に表示される<Activity>を追加してハイドレーションのパフォーマンスを向上させることができるそうです。これはなかなか面白そうですね。

一方、Vue.jsの公式サイトには以下のように書かれています。

The difference is that an element with v-show will always be rendered and remain in the DOM; v-show only toggles the display CSS property of the element.

引用元:Conditional Rendering | Vue.js

Vue.jsのv-showは、単にCSSのdisplayプロパティを切り替えるだけであり、再レンダリングの優先度の違いなどについては特に言及がされていません。

まとめ

Vue.jsのv-show

  • CSSのdisplayプロパティで表示・非表示を切り替える
  • レンダリングの優先度の違いはない

実際にソースコードを見てみると確かにdisplayプロパティを切り替えて、transitionがあればtransitionを適用しているだけでした。 github.com

Reactの<Activity>

  • CSSのdisplayプロパティで表示・非表示を切り替える
  • 再度表示されるときには子コンポーネントのStateが復元され、Effectsも再作成される
  • レンダリングの優先度が他のコンテンツより低くなる
  • つまり表示・非表示の切り替えやStateの保持以外にも、パフォーマンス面でのメリットがある

また、今後さまざまなユースケースに対応するために<Activity>にさらに多くのモードを追加する予定だそうです。楽しみですね!

In the future, we plan to add more modes to Activity for different use cases.

引用元:React 19.2 – React

より詳細な実装が気になる方は以下を見てみると良さそうです。

公式ドキュメント

<Activity>の具体的な使用例や詳細な説明が記載されています。

react.dev

Reactのソースコード

以下の関数が関係していそうです。

  • mountActivityChildren
  • retryActivityComponentWithoutHydrating
  • mountDehydratedActivityComponent
  • updateDehydratedActivityComponent
  • updateActivityComponent

github.com

最後に

Legalscapeでは、ReactやVue.jsを使ったサービス開発を行っています。またフロントエンドのみならず、バックエンド、インフラ、AIの最新技術をキャッチアップし、試行錯誤しています。同時に、技術に固執せず、サービス価値の最大化に向けて議論と実践を重ねています。
少しでも興味を持っていただけた方は、ぜひ下のバナーから採用情報をご覧ください。