💼

React19 use Hookについて

2024/03/28に公開

use Hookとは

use hookは、Reactのバージョン19に導入される新しいhookで、コンテキストとプロミスを使う時に便利です。
ReactではuseStateuseEffectのようなHookをif文の内部で使用することができませんでしたが、(ReactのHookルールに違反するため)use hookはループやifのような条件文の中で呼び出すことができます。

サーバーコンポーネントからデータを取得し、クライアントコンポーネントに渡すことができるRSC(React Server Component)が登場しました。ここでuse hookを使用すると、プロミスとコンテキストから値を取得できます。

RSCとは

RSCは、React Server Componentの略で、サーバーでレンダリングされるコンポーネントを意味します。サーバーコンポーネントはasync関数として宣言され、内部でawaitを使用して非同期データを取得し、それをレンダリングすることが可能です。非同期コンポーネントになります。

  • RSCでは、useStateやuseEffectのようにクライアントで使用されるフックは使用できません。(シリアライズが必要なため)
  • RSCでは、onClickやonFocusのようなイベントハンドラーは使用できません。(シリアライズが必要なため)
    RSCのレンダリングが完了するまで、クライアントではReactのSuspenseを使用してRSCの位置に別のコンポーネントを表示することが可能です。
  • RSCで使用されるパッケージは全てサーバーでimportされ、サーバーでのみ使用されるため、JSバンドルとしてクライアントに伝達されません。

use hookを使用してプロミスを処理する

// server component
import { fetchproductData } from './api.js';
import { ProductCard } from './ProductCard.jsx';

const App = () => {
  const productPromise = fetchproductData();
  return <ProductCard productPromise={productPromise} />;
};

export default App;

上記のコードを見ると、サーバーコンポーネントからproductDataを取得し、クライアントコンポーネントに渡しています。これで、クライアントコンポーネントではuse hookを使用してこのプロミスを利用できます。

'use client';

import { use } from 'react';

export function ProductCard ({ productPromise }) {
  const productList = use(productPromise);
  return <p>Here is the message: {productList}</p>;
}

ここではuse hookを使用してプロミスからproductを取得し、クライアントコンポーネントで使用しています。このように読み込む際にエラーのハンドリングはどうするのかと思いますが、クライアントコンポーネントをSuspenseError Boundaryを使って、ローディングとエラー状態を処理します。

import { use, Suspense } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { fetchproductData } from './api.js';
import { ProductCard } from './ProductCard.jsx';

const App = () => {
  const productPromise = fetchproductData();

  return;
  <ErrorBoundary fallback={<p>エラーが発生しました。</p>}>
    <Suspense fallback={<p>Loading...</p>}>
      <ProductCard productPromise={productPromise} />
    </Suspense>
  </ErrorBoundary>;
};

export default App;

エラー処理にError Boundaryを使用したくない場合、Reactはサーバーコンポーネントでエラーを発見し、クライアントコンポーネントに伝える方法を提供しています。↓

import { use, Suspense } from 'react';
import { fetchproductData } from './api.js';
import { ProductCard } from './ProductCard.jsx';

const App = () => {
  const productPromise = new Promise((resolve, reject) => {
    fetchproductData()
      .then(res => resolve(res))
      .catch(error => {
        resolve(error.message || 'エラーが発生しました。');
      });
  });

  return (
    <Suspense fallback={<p>Loading...</p>}>
        <ProductCard productPromise={productPromise} />
    </Suspense>
  );
};

export default App;

useフックを使用してコンテキストを処理する

Contextから値を取得するためにはuseContextを使用する必要がありました。
今ではuse hookを使ってクライアントコンポーネントでContextの値を使用できます。
AppContextを使用してUIのテーマを切り替える例です。
Contextは現在のテーマ(ライトテーマとダークテーマ)とテーマを切り替える関数を提供します。
クライアントコンポーネントでは、use hookを使用してこのContextの値を取得し、現在のテーマに応じてUIのスタイルを変更し、ボタンクリックでテーマを切り替えることができます。

'use client';

import { createContext, useState } from 'react';

export const ThemeContext = createContext();

export const ThemeProvider = ({ children }) => {
  const [theme, setTheme] = useState('light'); 

  const toggleTheme = () => {
    setTheme((currentTheme) => (currentTheme === 'light' ? 'dark' : 'light'));
  };

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      {children}
    </ThemeContext.Provider>
  );
};

クライアントコンポーネントでは、次のようにuse hookを使用してテーマを適用し、変更することができます。

'use client';

import { ThemeContext } from '@/contexts/theme-context';
import { use } from 'react';

const ThemeSwitcher = () => {
  const { theme, toggleTheme } = use(ThemeContext);

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff', padding: '10px' }}>
      <h1>This is a {theme} theme!</h1>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

export default ThemeSwitcher;

if文の中でuse hookを使用する例です。ユーザーがアクティブな場合にのみテーマ切り替えボタンを表示する例です。

'use client';

import { useState } from 'react';
import { ThemeContext } from '@/contexts/theme-context';
import { use } from 'react';

const ConditionalThemeSwitcher = () => {
  const [isActive, setIsActive] = useState(false);
  const { theme, toggleTheme } = use(ThemeContext);

  if (!isActive) {
    return (
      <div>
        <h1>Activate theme switcher to see the button</h1>
        <button onClick={() => setIsActive(true)}>Activate</button>
      </div>
    );
  }

  return (
    <div style={{ background: theme === 'light' ? '#fff' : '#333', color: theme === 'light' ? '#000' : '#fff', padding: '10px' }}>
      <h1>This is a {theme} theme!</h1>
      <button onClick={toggleTheme}>Toggle Theme</button>
    </div>
  );
};

export default ConditionalThemeSwitcher;

このコードは、isActiveの状態に応じてテーマ切り替えボタンを表示または非表示にします。
use hookを使用して条件付きレンダリングを処理できることを示す例です。

結論

use hookの導入は、React19で開発者がサーバーコンポーネントとクライアントコンポーネント間のデータ交換をより効率的に扱えるようにする重大な変化だと思います。この新しいhookを通じて、今ではif文やループのようなプログラミング構造内でもhookを使用できるようになり、特に、プロミスとコンテキストをよりシンプルで直感的に扱えるようになり、データローディングや状態管理などの作業をより簡潔で効果的に行えるようになります。

また、サーバーコンポーネント(RSC)との組み合わせは、サーバーサイドレンダリングを活用するReactアプリケーションのパフォーマンスとユーザーエクスペリエンスを大幅に向上させると思います。
サーバーでレンダリングされたコンポーネントをクライアントに効率的に送信し、クライアントでは必要なデータや状態をサーバーから受け取り、即座に使用できるようになることで、アプリケーションの初期ローディング時間を短縮し、全体的な反応速度を改善できます。

Discussion