はじめに
こんにちは。ニフティの山田です。
10/1にReact 19.2がリリースされました。
最近のReactはServer Componentに関わる変更が多いのですが、今回はそれ以外にもActivity・useEffectEventという2種のAPIが追加になっていますので、本記事で解説しようとおもいます。
Activity
コンポーネント出し分けの問題
従来、コンポーネントの表示・非表示ををフラグなどの状態に応じて出し分けるには以下の方法がありましたが、いずれも問題がありました。
コンポーネントごとアンマウントする方法
|
1 |
{ enabled && <MyComponent /> } |
最も標準的で、最初に考えつく方法かと思います。大抵の場合はこれで良かったのですが、以下の問題がありました。
- 再レンダリングが重い
- コンポーネントごと消えてしまうため、再度表示する際に1からレンダリングする必要があり、計算量が重い
- stateが消失する
- stateはコンポーネントに紐づくため、アンマウントされると消えてしまう
- フォームなどがあった場合、入力されたデータは消えることになる
- 状態管理ライブラリなどでglobal stateとして持てば解決はできるものの、局所状態の管理としては過剰
CSSで非表示にする方法
|
1 |
<MyComponent style={enabled ? undefined : { display: 'none' }}> |
アンマウントされるのがだめなら、要素は残したままCSSで消せばいいということになりますが、こちらも問題があります。
- useEffectが走ってしまう
- データロード・通知などをuseEffectで行っていた場合、実行されてしまう
Activityの動作
以上を解決できるものとして導入されたのがActivityです。childrenをとるコンポーネントであり、以下のように使います。
|
1 2 3 |
<Activity mode={enabled ? 'visible' : 'hidden'}> <MyComponent /> </Activity> |
modeの値に応じてchildrenコンポーネントの表示・非表示が決まります。
modeは現時点で2種類あり、以下のような動作となります(将来的に追加の可能性もあるようです)。
- visible
- childrenコンポーネントが表示される
- useEffectなども動作する
- hidden
- childrenコンポーネントは非表示となる
- useEffectは停止する
- バックグラウンドで優先度低でレンダリングされる
これにより、非表示時に
- 裏側でレンダリングが可能であり
- stateは維持しつつ
- effectは動作させない
という動作が可能となりました。
必要があって生まれた機能ではありますが、Reactの「ロジックが(特別な記法なく)JavaScriptとして読める」という純粋性からは遠ざかる変更でもあります
Vue界隈からは「これ v-if では?」と言われていたりもするようです
useEffectEvent
useEffectでイベントを発火させる際の課題
useEffectでイベントトリガーを仕込む場合、従来は課題がありました。
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
function ChatRoom({ roomId, theme }) { useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { showNotification('Connected!', theme); }); connection.connect(); return () => { connection.disconnect() }; }, [roomId, theme]); ... } |
以上はReact公式ブログの例を用いています。
これはコンポーネントがマウントされるとWebSocket的なものでチャットに繋がるサンプルですが、以下の問題があります。
- 依存配列にthemeが含まれているので、themeを変えただけでuseEffectが走り、再接続が実行されてしまう
- themeを依存配列から外すと、イベントハンドラが更新されず、通知のthemeが更新されなくなる
このように、最新のprops・stateを参照し続けたいのですが、useEffectを再実行したくはない、という場合の対処が困難でした。
useEffectEventの動作
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
function ChatRoom({ roomId, theme }) { const onConnected = useEffectEvent(() => { showNotification('Connected!', theme); }); useEffect(() => { const connection = createConnection(serverUrl, roomId); connection.on('connected', () => { onConnected(); }); connection.connect(); return () => connection.disconnect(); }, [roomId]); ... } |
useEffectEventは新しく追加されたフックであり、イベントハンドラをラップする形で使用します。
useEffectEventを通すことで
- イベントハンドラ実行時にはuseEffectEventが常に最新のprops・stateを参照し
- useEffectの依存配列に含めなくてよい
という動作が実現できるようになりました。
本フックはExperimental機能として以前から存在していましたが、React 19.2でstableになった形となります
おわりに
本記事では、Reactの基本的なAPIとして追加されたActivity・useEffectEventの2種について解説しました。
既存コードにも無理なく導入できるものですので、今まで困っていたような方は導入を検討してみてはいかがでしょうか。


