あまり知られていないReactの便利な機能

Reactは、コンポーネントを利用して再利用性の高いコードを書ける強力なフロントエンドライブラリですが、日々更新されるエコシステムの中で、意外と知られていない便利な機能も多く存在します。この記事では、そんな「知ると役立つ」Reactの機能について、具体的なコード例を交えながらご紹介しようと思います。

### 1. `React.memo`によるコンポーネントのレンダリング最適化

`React.memo`は、コンポーネントが不必要に再レンダリングされるのを防ぐための機能です。通常、親コンポーネントが再レンダリングされると子コンポーネントも再レンダリングされますが、子コンポーネントが特に更新される必要がない場合でも再レンダリングされることがあります。`React.memo`を使うことで、親コンポーネントが再レンダリングされても、子コンポーネントのプロパティに変更がない限り再レンダリングを抑制することができます。

```javascript
import React from 'react';

// 普通の関数コンポーネント
const ExpensiveComponent = ({ value }) => {
    console.log("ExpensiveComponentが再レンダリングされました！");
    return <div>{value}</div>;
};

// React.memoを使ったメモ化
const MemoizedComponent = React.memo(ExpensiveComponent);

export default function App() {
    const [count, setCount] = React.useState(0);

    return (
        <div>
            <h1>カウント: {count}</h1>
            <button onClick={() => setCount(count + 1)}>増やす</button>
            <MemoizedComponent value="メモ化された値" />
        </div>
    );
}
```

上記のコードでは、`MemoizedComponent`が`React.memo`でラップされているため、`count`が変わっても`MemoizedComponent`は再レンダリングされません。これにより、パフォーマンスが向上します。

### 2. `React.lazy`と`Suspense`によるコード分割（Code Splitting）

Reactでは、`React.lazy`と`Suspense`を使うことで、必要な時にのみコンポーネントをロードする「遅延読み込み」が可能です。特に、アプリが大規模になると初回ロードの速度が遅くなりがちですが、`React.lazy`を活用することで、特定のコンポーネントのみ遅延ロードし、パフォーマンス改善を図ることができます。

```javascript
import React, { Suspense } from 'react';

// React.lazyを使用してコンポーネントを遅延読み込み
const LazyLoadedComponent = React.lazy(() => import('./LazyLoadedComponent'));

function App() {
    return (
        <div>
            <h1>React.lazyとSuspenseの例</h1>
            <Suspense fallback={<div>Loading...</div>}>
                <LazyLoadedComponent />
            </Suspense>
        </div>
    );
}

export default App;
```

この例では、`LazyLoadedComponent`は`Suspense`内でラップされています。これにより、`LazyLoadedComponent`が読み込まれるまで`"Loading..."`と表示され、読み込みが完了すると実際のコンポーネントがレンダリングされます。これを使うことで、アプリ全体の初期読み込みが軽くなり、ユーザーにとってより快適な体験を提供できます。

### 3. `useCallback`と`useMemo`で無駄な計算を避ける

`useCallback`と`useMemo`は、不要な再計算や関数の再生成を防ぐためのフックです。これにより、特に高コストな計算が行われる場合にパフォーマンスを最適化できます。

#### `useCallback`の例

`useCallback`は、依存する値が変わらない限り、同じ関数インスタンスを再利用します。

```javascript
import React, { useState, useCallback } from 'react';

function App() {
    const [count, setCount] = useState(0);

    const handleClick = useCallback(() => {
        console.log('クリックされました！');
    }, []);

    return (
        <div>
            <h1>カウント: {count}</h1>
            <button onClick={() => setCount(count + 1)}>増やす</button>
            <button onClick={handleClick}>クリック</button>
        </div>
    );
}
```

この例では、`handleClick`は`useCallback`を使ってメモ化されています。`count`が変わっても`handleClick`は再生成されないため、不要なレンダリングを防ぐことができます。

#### `useMemo`の例

`useMemo`は、依存する値が変わらない限り、計算結果をキャッシュするために使用します。

```javascript
import React, { useState, useMemo } from 'react';

function App() {
    const [count, setCount] = useState(0);
    const [otherCount, setOtherCount] = useState(0);

    const expensiveCalculation = useMemo(() => {
        console.log("高コストな計算実行中...");
        return count * 2;
    }, [count]);

    return (
        <div>
            <h1>カウント: {count}</h1>
            <h1>高コストな計算結果: {expensiveCalculation}</h1>
            <button onClick={() => setCount(count + 1)}>増やす</button>
            <button onClick={() => setOtherCount(otherCount + 1)}>他のカウントを増やす</button>
        </div>
    );
}
```

上記では、`count`が変わらない限り`expensiveCalculation`は再計算されません。このように、`useMemo`を利用することで、特に高コストな処理を含むアプリケーションでのパフォーマンス向上が期待できます。

### 4. `context`での値のリセット - `resetContext`

通常、Reactの`Context API`を使ってデータをアプリ全体に渡すことができますが、コンテキストをリセットしたい場合、手動でリセットのコードを書く必要がありました。Reactの最新のアップデートで追加された`resetContext`を活用すると、シンプルにリセットできるようになります。

```javascript
import React, { createContext, useContext, useState } from 'react';

const CounterContext = createContext();

const CounterProvider = ({ children }) => {
    const [count, setCount] = useState(0);

    const resetContext = () => {
        setCount(0); // コンテキストの値をリセット
    };

    return (
        <CounterContext.Provider value={{ count, setCount, resetContext }}>
            {children}
        </CounterContext.Provider>
    );
};

const CounterDisplay = () => {
    const { count, resetContext } = useContext(CounterContext);
    return (
        <div>
            <h1>カウント: {count}</h1>
            <button onClick={resetContext}>リセット</button>
        </div>
    );
};

function App() {
    return (
        <CounterProvider>
            <CounterDisplay />
        </CounterProvider>
    );
}

export default App;
```

このコードでは、`resetContext`関数によってコンテキスト内の`count`が0にリセットされます。これにより、コードの可読性が向上し、再利用性の高いコンテキストプロバイダを簡単に実装できます。