createContext を深掘る

今回は[createContext](https://ja.react.dev/reference/react/createContext#createcontext)について学んでいきます。

>createContext は、コンポーネントが提供または読み取りできるコンテクストを作成するための関数です。

コンテクストという単語が出てきました。
ここで言うコンテクストとは、下記の事のようです。[リンク](https://ja.react.dev/learn/passing-data-deeply-with-context)

>コンテクスト (Context) を使用することで、親コンポーネントから props を明示的に渡さずとも、それ以下のツリー内の任意のコンポーネントが情報を受け取れるようにできます。

### createContextの使い方

```jsx
import { createContext } from 'react'; // 呼び出し
const ThemeContext = createContext('light'); //作成
```

引数を1つ持っていますね、これはデフォルトになります。

> コンポーネントがコンテクストを読み取るときに、その上のツリー内で対応するコンテクストプロバイダがない場合にコンテクストが持つ値です。デフォルト値が必要ない場合は null を指定します。デフォルト値は「最後の手段」として使われるように意図されています。これは静的な値であり、時間が経過しても変化しません。

決して変わることのない初期値ですね。

### 返り値

Providerと、Consumerの2つを返します。

>createContext はコンテクストオブジェクトを返します。
コンテクストオブジェクト自体は情報を持っていません。他のコンポーネントがどのコンテクストを読み取るか、または提供するかを表します。通常、上位のコンポーネントで SomeContext.Provider を使用してコンテクストの値を指定し、下位のコンポーネントで useContext(SomeContext) を呼び出してコンテクストを読み取ります。コンテクストオブジェクトにはいくつかのプロパティがあります：

(↑いきなり説明が書かれているので「ん？」となりますね)

#### Providerとは？

Providerコンポーネントはvalue属性を受け取り、これがコンテクストの現在の値を定義します。子コンポーネントはこの値にアクセスできます。

##### value属性?
> value: このプロバイダの内側（深さに関わらず）にあるコンポーネントがコンテクストを読み取る際に、渡したい値です。コンテクストの値は任意の型にすることができます。プロバイダ内で useContext(SomeContext) を呼び出しているコンポーネントは、それより上位かつ最も内側にある対応するコンテクストプロバイダの value を受け取ります。

Providerの入れ子にしたcomponetへ渡したい値ですね。

#### Consumerとは？

今は使わないようですね、useContextの古い書き方です。
> この古い方法はまだ動作しますが、新しく書かれたコードは useContext() を使ってコンテクストを読み取るべきです：

もう使うことはないでしょう。(useContextのことだなと分かればOK)

### 公式を引用してコードにしてみる

```jsx
//公式から引用をちょっと変更
import { createContext, useState, useContext } from 'react' // 追加

const ThemeContext = createContext('light');

function App() {
  const [theme, setTheme] = useState('light');
  // ...
  return (
    <ThemeContext.Provider value={theme}>
      <Page /> {/* ←Pageにthemeの値を渡したい */}
    </ThemeContext.Provider>
  );
}

const Page = () =>{
  const theme = useContext(ThemeContext) {/*ThemeContextの中にthemeが入っているのかな？*/}
  return <span>{theme}です。</span>
}

export default App;
```

## [使用法](https://ja.react.dev/reference/react/createContext#usage)

>コンテクストを利用することで、明示的に props を渡さずに、コンポーネントに深くまで情報を渡すことができます。

上記はそうですね、先ほど確認できました。
ここでは役に立たないuseContextの例が記載されていますね。(デフォルト値そのまま読み込んでいます)

```jsx
// 公式から引用
import { createContext } from 'react';
const ThemeContext = createContext('light');
const AuthContext = createContext(null);

function Button() {
  const theme = useContext(ThemeContext); // デフォルト値そのまま読み込んでる
  // ...
}
function Profile() {
  const currentUser = useContext(AuthContext); // デフォルト値そのまま読み込んでる
  // ...
}
```

上記の場合、定数を別途用意して使えば済みます。
コンテクストの真の利用方法ではありません。

> コンテクストが便利なのは、コンポーネントから動的な値を提供できるからです：

```jsx
//公式から引用
function App() {
  const [theme, setTheme] = useState('dark');
  const [currentUser, setCurrentUser] = useState({ name: 'Taylor' });

  // ...

  return (
    <ThemeContext.Provider value={theme}>
      <AuthContext.Provider value={currentUser}>
        <Page />
      </AuthContext.Provider>
    </ThemeContext.Provider>
  );
}
```

setThemeとsetCurrentUserがどのように使われているか書かれていません。
上記例をもうちょっと具体的に肉付けしてみますね。

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

// コンテクストの作成
const ThemeContext = createContext();
const AuthContext = createContext();

function App() {
  //公式ではトラブルシューティングで説明されていますが、stateを使って値を変えれるようにしよう！
  const [theme, setTheme] = useState('dark');
  const [currentUser, setCurrentUser] = useState({ name: 'Taylor' });

  // 関数に包むよ
  const login = (user) => {
    setCurrentUser(user);
  }

  // 関数に包むよ
  const toggleTheme = () => {
    setTheme(prevTheme => (prevTheme === 'dark' ? 'light' : 'dark'));
  }

  return (
    <ThemeContext.Provider value={{ theme, toggleTheme }}>
      <AuthContext.Provider value={{ currentUser, login }}>
        <Page />
      </AuthContext.Provider>
    </ThemeContext.Provider>
  );
}

const Page = () => {
  const { theme, toggleTheme } = useContext(ThemeContext);
  const { currentUser, login } = useContext(AuthContext);

  return (
    <div>
      <h2>現在のテーマ: {theme}</h2>
      <button onClick={toggleTheme}>テーマ切替</button>
      <h2>現在のユーザー: {currentUser.name}</h2>
      <button onClick={() => login({ name: '新しいユーザー' })}>ログイン切替</button>
    </div>
  );
}
```

※setThemeとsetCurrentUserを一度関数に包んでいるのは、操作の前後に追加の処理を挟みたい場合などに便利だからです。

----

お疲れ様でした！
今回はcreateContextを見ていきました！

次回は別のReact フックも深く探ってみようと思います。