---
title: "React Context API：状態を簡単に管理する"
description: "状態を新しい React Context API で管理するのは非常に簡単です。Redux との違いは何で、それをどのように実用的なチュートリアルで使うかについて学びましょう"
authors:
  - name: "Abdulazeez Adeshina"
    url: "https://auth0.com/blog/authors/abdulazeez-adeshina/"
date: "Aug 14, 2018"
category: "Developers,Tutorial,React"
tags: ["react", "context-api", "redux", "state", "state-management", "frontend", "javascript"]
url: "https://auth0.com/blog/jp-react-context-api-managing-state-with-ease/"
---

# React Context API：状態を簡単に管理する



**TL;DR:** React Context API は React のエコシステムでは新しいものではありません。ただし、React  の  `16.3.0`  リリース では  [API に多くの改善がなされています](https://auth0.com/blog/whats-new-in-react-16-3/)。これら改善は予想以上のもので、Redux やその他高度な状態を管理するライブラリの必要性が大きく提言されます。本書では、実用的なチュートリアルを通して新しい React Context API が小型の React アプリケーションに対する Redux への必要性をどのように置き換えるかを学んでいきます。

<include src="JpTweetQuote" quoteText="この実用的なチュートリアルでどのように Redux から新しい React Context API に移行するかについて学びましょう"/>

## Redux についてのクイックレビュー

React Context API に進む前に、両者を比較できるように Redux についてのクイックレビューをする必要があります。[Redux は状態の管理を容易にする JavaScript ライブラリです](https://redux.js.org/)。Redux は React 自体には結びつきません。世界中の開発者は  React  や  Angular  といった人気の高い JavaScript フロントエンド フレームワークと共に Redux を使ってきました。

誤解のないように言うと、本書で言う状態の管理とはシングルページアプリ（SPA）で発生する特定のイベントが発生したときの変更を処理することです。例えば、ボタンのクリックやサーバーから来る非同期メッセージのようなイベントがアプリの状態への変更をトリガーします。

Redux では、特に、次の点をご留意ください。

1. アプリ全体の状態が単一のオブジェクト（別名「頼できる情報源(Source of truth)」）に格納されます。
2. 状態を変更するには、発生する必要があることを説明する  `actions`  をディスパッチする必要があります。
3. オブジェクトのプロパティを変更したり、既存の配列を変更することはできません。Redux では、新しいオブジェクトまたは新しい配列に常に新しい参照を返さなければなりません。

Redux についてあまりご存知でない方や詳細を学びたい方は、[Redux についての実用的なチュートリアル](https://auth0.com/blog/redux-practical-tutorial/)をご覧ください。

## React Context API の導入

React Context API はあらゆるレベルに手動で `props`  を伝えなくても、コンポーネント ツリーを通してデータを送信する方法を提供します。React では、親コンポーネントからその子コンポーネントにプロパティとして送信されることがよくあります。

新しい React Context API を使うことは次の 3 つのステップによって決まります。

1. 初期状態を `React.createContext` に送ります。この機能は  `Provider`  や  `Consumer`  と共にひとつのオブジェクトを返します。
2. `Provider`  コンポーネントをツリーのトップで使用し、`value`  と呼ばれるプロパティを承諾させます。この値は何でも構いません。
3. 状態のサブセットを取得するために、`Consumer`  コンポーネントをコンポーネント ツリーにあるプロバイダーの下で使います。

ご覧のように、関係する概念は Redux とはそんなに異なりません。実際、Redux はそのパブリック API の下で React Context API を使用します。ただし、最近 Context API は一般人でも十分使用できるほどその成熟度が高まりました。

## React アプリを Redux で作る

前述したように、本書の目的は新しい Context API を小さなアプリ向け Redux と置き換える方法をお伝えすることです。よって、シンプルな React アプリを Redux で作り、その後、React Context API を活用できるようにこの状態管理ライブラリを削除する方法について学んでいきます。

これから構築するシンプルなアプリケーションは人気の高い食べ物やその起源のリストを処理するアプリです。このアプリには、ユーザーがあるキーワードを基にしたリストをフィルターできる検索機能も含まれています。

最終的に、次のようなアプリになります。

### プロジェクトの要件

本書では React と一部の NPM ライブラリのみを使用しますので、開発マシンには Node.js と NPM がインストールされていれば大丈夫です。Node.js や NPM がまだインストールされていない場合は、[公式のインストール手順](https://nodejs.org/en/download/)をご覧いただき、両方をインストールしてください。

これら依存関係をインストールしたら、`create-react-app`  ツールをインストールする必要があります。このツールは開発者が React で作業を開始するときに役立ちます。ですから、これをインストールするには、端末を開いて、次のコマンドを実行します。

```bash
npm i -g create-react-app
```

### React アプリをスキャフォールディングする

`create-react-app`  をインストールしたら、プロジェクトを配置するディレクトリを移動し、次のコマンドを実行します。

```bash
create-react-app redux-vs-context
```

数秒後、`create-react-app`  がアプリの作成を終えます。その後、このツールで作成した新しいディレクトリに移動し、Redux をインストールします。

```bash

# プロジェクトに移動します
cd redux-vs-context

# Redux をインストールします
npm i --save redux react-redux
```

**注：** `redux`  はメインライブラリで、`react-redux`  は React と Redux の間の対話を容易にするライブラリです。簡単に言うと、後者は React と Redux の間でプロキシとして機能します。

### React アプリを Redux で作る

これで React アプリの構成ができ、Redux をインストールしたので、お好みの IDE でプロジェクトを開きます。そこから、次の３つのファイルを  **src**  ディレクトリに創ります。

- `foods.json`：このファイルは食べ物の静的配列とその起源を保持します。
- `reducers.js`：このファイルはアプリの Redux バージョンの状態を管理します。
- `actions.js`：このファイルはアプリの Redux バージョンの状態で変更ををトリガーする機能を保持します。

では、始めるには  `foods.json`  ファイルを開き、次のコンテンツをそれに加えます。

```json
[
  {
    "name": "Chinese Rice",
    "origin": "China",
    "continent": "Asia"
  },
  {
    "name": "Amala",
    "origin": "Nigeria",
    "continent": "Africa"
  },
  {
    "name": "Banku",
    "origin": "Ghana",
    "continent": "Africa"
  },
  {
    "name": "Pão de Queijo",
    "origin": "Brazil",
    "continent": "South America"
  },
  {
    "name": "Ewa Agoyin",
    "origin": "Nigeria",
    "continent": "Africa"
  }
]
```

ご覧のように、このファイルには特に特別なものはありません。単なる異なる国の異なる食べ物の配列です。

`Foods.json`  ファイルを定義した後、Redux ストアを定義することを重点的に取り組みます。まとめると、`store`  はアプリの状態の信頼できる唯一の情報源（Single source of truth）を保持する場所です。そこで、`reducers.js`  ファイルを開き、次のコードを加えます。

```javascript
import Food from "./foods";

const initialState = {
  food: Food,
  searchTerm: ""
};

export default function reducer(state = initialState, action) {
  // アクションの種類によって切り替えます
  switch (action.type) {
    case "SEARCH_INPUT_CHANGED":
      const { searchTerm } = action.payload;
      return {
        ...state,
        searchTerm: searchTerm,
        food: searchTerm
          ? Food.filter(
              food =>
                food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1
            )
          : Food
      };
    default:
      return state;
  }
}
```

上記のコードでは、ご覧のように `state`  と  `action`  の２つのパラメーターを受ける  `reducer`  機能があります。React アプリケーションを始めるとき、この機能はその直前で定義された  `initialState`  を取得し、アクションのインスタンスをディスパッチするとき、この機能は現在の状態（もはや  initialState  ではない）を取得します。それから、これらアクションのコンテンツを基に、`reducer`  機能はアプリの新しい状態を作ります。

次に、これらアクションが何かを定義します。実際、これをシンプルにするには、ユーザーがアプリに検索語句を入力したときにトリガーする１つのアクションを定義します。そこで  `actions.js`  ファイルを開き、次のコードを挿入します。

```javascript
function searchTermChanged(searchTerm) {
  return {
    type: "SEARCH_INPUT_CHANGED",
    payload: { searchTerm }
  };
}

export default {
  searchTermChanged
};
```

これで  `action`  クリエータができましたから、次にやることは  `react-redux`  で利用可能な  `App`  コンポーネントを  `Provider`  コンポーネントにラップすることです。このプロバイダーは React アプリを信頼できる唯一の情報源（Single source of truth）（例：`store`）にする担当です。

このプロバイダーを使うには、まず、`reducers.js`  ファイルで定義された  `initialState`  を使ってアプリの  `store`  を作ります。それからこの  `store`  を  `Provider`  の助けを得て  App  に送ります。これら作業を達成するには、`index.js`  ファイルを開き、そのコンテンツと次を置き換えます。

```javascript
import React from "react";
import ReactDOM from "react-dom";
import { Provider } from "react-redux";
import { createStore } from "redux";
import reducers from "./reducers";
import App from "./App";

// Rducer 情報を使ってストアを作ります。
// それは Rducer が Redux Store の文書パーツだからです。
const store = createStore(reducers);

ReactDOM.render(
  <Provider store={store}>
    <App />
  </Provider>,
  document.getElementById("root")
);
```

これだけです！これで React アプリに Redux を構成し終えました。ここで、UI（ユーザー インターフェイス）を実装しますから、ユーザーはこの章で実装される機能を使用します。

### React インターフェイスを構築する

これでアプリケーションのコアが出来上がったので、ユーザー インターフェイスの構築に取り組みます。そのためには  `App.js`  ファイルを開き、そのコンテンツと次を置き換えます。

```javascript
import React from "react";
import { connect } from "react-redux";
import actions from "./actions";
import "./App.css";

function App({ food, searchTerm, searchTermChanged }) {
  return (
    <div>
      <div className="search">
        <input
          type="text"
          name="search"
          placeholder="Search"
          value={searchTerm}
          onChange={e => searchTermChanged(e.target.value)}
        />
      </div>
      <table>
        <thead>
          <tr>
            <th>Name</th>
            <th>Origin</th>
            <th>Continent</th>
          </tr>
        </thead>
        <tbody>
          {food.map(theFood => (
            <tr key={theFood.name}>
              <td>{theFood.name}</td>
              <td>{theFood.origin}</td>
              <td>{theFood.continent}</td>
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
}

export default connect(
  store => store,
  actions
)(App);
```

Redux を使ったことがない方がご存知ないかもしれないのは  `App`  コンポーネントをカプセル化するために使用する  `connect`  機能だけです。この機能は[高次コンポーネント（HOC)](https://reactjs.org/docs/higher-order-components.html) で、アプリと Redux  をつなげる接着剤のような役目を果たします。

ここでアプリを実行すれば、ブラウザーで使用できます。

```bash
npm run start
```

ただし、ご覧のようにアプリの見掛けはよくありません。そこで、それを改善するために、`App.css`  ファイルを開き、そのコンテンツと次を置き換えます。

```css
table {
  width: 100%;
  border-collapse: collapse;
  margin-top: 15px;
  line-height: 25px;
}

th {
  background-color: #eee;
}

td,
th {
  text-align: center;
}

td:first-child {
  text-align: left;
}

input {
  min-width: 300px;
  border: 1px solid #999;
  border-radius: 2px;
  line-height: 25px;
}
```

![React app implemented with Redux](https://images.ctfassets.net/23aumh6u8s0i/1u1ImuQtARWbJa1nRYqAhW/65c6f6e215d597668ca8d246cd78e204/react-app-with-redux)

これだけです！これで基本的な React と Redux のアプリができたので、Context API をどのように移行するかについて学びます。

## React アプリを React Context API で実装する

本章では、アプリの Redux バージョンを React Context API に移行する方法について学んでいきます。

幸運なことに、ご覧のように、Redux と Context API 間を切り替えて何度もリファクタリングする必要はありません。

初心者のために、アプリから Redux のあらゆるトレースを削除する必要があります。そのためには、端末から  `redux`  と  `react-redux`  のライブラリを両方とも削除します。

```bash
npm rm redux react-redux
```

その後、これらライブラリを参照する  `import`  ステートメントを削除します。ですから  `App.js`  ファイルを開き、次の行を削除します。

```javascript
import { connect } from "react-redux";
import actions from "./actions";
```

それから、このファイルの最後の行（`export default` で始まる行）と次を置き換えます。

```javascript
export default App;
```

これら変更が終わったら、Context API でアプリを再書き込みします。

### Redux から React Context API へ移行する

Redux を利用した元のアプリから Context API を利用するアプリに変えるには、アプリのデータを格納するコンテキストが必要です（このコンテキストが Redux Store を置き換えます）。また、`state`、`props`、および通常の React コンポーネント ライフサイクルがある `Context.Provider`  コンポーネントが必要です。

よって、`src`  ディレクトリに  `providers.js`  ファイルを作り、次のコードを加えます。

```js

import React from 'react';
import Food from './foods';

const DEFAULT_STATE = { allFood: Food, searchTerm: '' };

export const ThemeContext = React.createContext(DEFAULT_STATE);

export default class Provider extends React.Component {
  state = DEFAULT_STATE;
  searchTermChanged = searchTerm => {
    this.setState({searchTerm});
  };

  render() {
    return (
      <ThemeContext.Provider value={{
        ...this.state,
        searchTermChanged: this.searchTermChanged,
      }}> {this.props.children} </ThemeContext.Provider>);
  }
}

```

上記のコードで定義した  `Provider`  クラスは  `ThemeContext.Provider` 内にその他のコンポーネントをカプセル化する担当です。そうすることで、これらコンポーネントがアプリの状態や、この状態を変更する方法を提供する  `searchTermChanged`  機能にアクセスできます。

コンポーネント ツリーの値を後で消費するには、`ThemeContext.Consumer`  コンポーネントを始める必要があります。このコンポーネントは思いのままに使用する引数として上記の値  `props`  を受けるレンダー関数が必要です。

では、次に、`src`  ディレクトリ内に  `consumer.js`  と呼ばれるファイルを作り、次のコードを書き込みます。

```js

import React from 'react';
import {ThemeContext} from './providers';

export default class Consumer extends React.Component {
  render() {
    const {children} = this.props;

    return (
      <ThemeContext.Consumer>
        {({allFood, searchTerm, searchTermChanged}) => {
          const food = searchTerm
            ? allFood.filter(
              food =>
                food.name.toLowerCase().indexOf(searchTerm.toLowerCase()) > -1
            )
            : allFood;

          return React.Children.map(children, child =>
            React.cloneElement(child, {
              food,
              searchTerm,
              searchTermChanged,
            })
          );
        }}
      </ThemeContext.Consumer>
    );
  }
}

```

ここで、移行を終わらせるために、`index.js`  ファイルを開き、 `render()`  関数内で  `App`  コンポーネントを  `Consumer`  コンポーネントでラップします。また、`Provider`  コンポート内で  `Consumer`  をラップします。そうすると、次のようになります。

```javascript
import React from "react";
import ReactDOM from "react-dom";
import Provider from "./providers";
import Consumer from "./consumer";
import App from "./App";

ReactDOM.render(
  <Provider>
    <Consumer>
      <App />
    </Consumer>
  </Provider>,
  document.getElementById("root")
);
```

これだけです！これで Redux から React Context API への移行が終わりました。今、アプリを実行すると、全体が以前のように機能していることが分かります。ここでの違いはアプリは Redux を使っていないということです。

<include src="JpTweetQuote" quoteText="新しい React Context API は小さな React アプリケーションの Redux に代わる素晴らしいものです。"/>

## まとめ

Redux は大規模な React アプリを構築するときに使用すべき、`高度な`状態の管理ライブラリです。一方、Context API は小規模の React アプリで使用でき、バイト サイズで変更が可能です。Context API を使うと、状態の変化が示されたロジックを処理するために `reducers`、`actions` のようなコードをたくさん書く必要がありません。

---
