ReactのコンテキストAPIの紹介

公開: 2022-03-10
クイックサマリー↬この記事では、小道具のドリルに頼ることなく、Reactアプリでグローバルアプリケーションの状態を管理できるReactのコンテキストAPIの使用方法を学習します。

このチュートリアルでは、フックを正しく理解している必要があります。 それでも、始める前に、それらが何であるか、そしてこの記事で使用するフックについて簡単に説明します。

React Docsによると:

フックはReact16.8に新しく追加されたものです。 クラスを作成しなくても、状態やその他のReact機能を使用できます。」

それが基本的にReactフックです。 これにより、機能コンポーネントで状態、参照、その他のReact機能を使用できるようになります。

この記事で遭遇する2つのフックについて説明しましょう。

useStateフック

useStateフックを使用すると、機能コンポーネントで状態を使用できます。 useStateフックは、状態の初期値を唯一の引数として受け取り、2つの要素の配列を返します。 最初の要素は状態変数であり、2番目の要素は状態変数の値の更新を使用できる関数です。

次の例を見てみましょう。

 import React, {useState} from "react"; function SampleComponent(){ const [count, setCount] = useState(0); }

ここで、 countは状態変数であり、その初期値は0ですが、 setCountはcountの値を更新するために使用できる関数です。

useContextフック

これについては記事の後半で説明しますが、このフックを使用すると、基本的にコンテキストの値を使用できます。 これが実際に何を意味するのかは、この記事の後半でさらに明らかになります。

糸のワークスペース

Yarnワークスペースでは、モノリシックリポジトリ(monorepo)を使用してプロジェクトコードベースを整理できます。 Reactは、monorepoであり、Yarnワークスペースを使用してその目的を達成するオープンソースプロジェクトの良い例です。 関連記事を読む→

ジャンプした後もっと! 以下を読み続けてください↓

なぜコンテキストAPIが必要なのですか?

Reactアプリのライトモードとダークモードを切り替える「テーマトグル」コンポーネントを構築したいと考えています。 すべてのコンポーネントは、それに応じてスタイルを設定できるように、現在のテーマモードにアクセスできる必要があります。

通常、小道具を介してすべてのコンポーネントに現在のテーマモードを提供し、 stateを使用して現在のテーマを更新します。

 import React from "react"; import ReactDOM from "react-dom"; function App() { return ( <div> <Text theme= "blue" /> <h1>{theme}</h1> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

上記のコードサンプルでは、 h1要素をレンダリングするテキストコンポーネントを作成しました。 h1要素の色は、現在のテーマモードによって異なります。 現在、テーマは青です。 stateを使用して、 blueredのテーマを切り替えることができます。

useStateフックを使用して、「theme」という状態を作成します。 useStateフックは、テーマの現在の値と、テーマを更新するために使用できる関数を返します。

それでは、テーマの状態を作成しましょう。

 const [theme, setTheme] = React.useState("blue");

また、 Appコンポーネントにボタン要素を追加します。 このボタンはテーマを切り替えるために使用され、クリックイベントハンドラーが必要です。 それでは、クリックイベントハンドラーを次のように記述しましょう。

 const onClickHandler = () => { setTheme(); }

ここで、現在のテーマがBlueの場合は新しいテーマをRedに設定し、その逆も同様です。 ifステートメントを使用する代わりに、これを行うためのより便利な方法は、JavaScriptの三項演算子を使用することです。

 setTheme( theme === "red"? "blue": "red");

これで、 onClickハンドラーを作成しました。 このボタン要素をAppコンポーネントに追加しましょう。

 <button onClick = {onClickHandler}>Change theme</button>

また、Textコンポーネントのテーマプロップの値をテーマの状態に変更してみましょう。

 <Text theme={theme}/>

今、私たちはこれを持っている必要があります:

 import React from "react"; import ReactDOM from "react-dom"; import "./styles.css"; function App() { const[theme, setTheme] = React.useState("red"); const onClickHandler = () => { setTheme( theme === "red"? "blue": "red"); } return ( <div> <Text theme={theme}/> <button onClick = {onClickHandler}>Change theme</button> </div> ); } function Text({theme}) { return( <h1 style = {{ color: `${theme}` }}>{theme}</h1> ); } const rootElement = document.getElementById("root"); ReactDOM.render(<App />, rootElement);

これで、2つのテーマを切り替えることができます。 ただし、これがはるかに大きなアプリケーションである場合、深くネストされたコンポーネントでテーマを使用することは困難であり、コードは扱いにくくなります。

コンテキストAPIの紹介

コンテキストAPIを紹介します。 Reactのドキュメントによると:

「コンテキストは、すべてのレベルで小道具を手動で渡すことなく、コンポーネントツリーを介してデータを渡す方法を提供します。」

より詳細な定義については、コンポーネントがどれほど深くネストされていても、コンポーネントツリー全体のすべてのコンポーネントで特定のデータを利用できるようにする方法を提供します。

この例を見てみましょう:

 const App = () => { return( <ParentComponent theme = "light"/> ); } const ParentComponent = (props) => ( <Child theme = {props.theme} /> ) const Child = (props) => ( <Grandchild theme = {props.theme} /> ) const Grandchild = (props) => ( <p>Theme: {props.theme}</p> )

上記の例では、 themeというParentComponentの小道具を使用してアプリケーションテーマを指定しました。 その小道具をコンポーネントツリーのすべてのコンポーネントに渡して、 GrandChildコンポーネントである必要な場所に配置する必要がありました。 ChildComponentはテーマの小道具とは何の関係もありませんでしたが、仲介者として使用されました。

ここで、 GrandChildコンポーネントが、上の例よりも深くネストされていると想像してください。 ここで行ったのと同じ方法でテーマの小道具を渡す必要があり、面倒です。 これは、 Contextが解決する問題です。 Contextを使用すると、コンポーネントツリー内のすべてのコンポーネントは、コンテキストに配置することを決定したデータにアクセスできます。

Contextを始めましょう

記事の冒頭で作成したテーマ切り替えボタンをContextAPIを使用して複製するときが来ました。 今回は、テーマトグルを別のコンポーネントにします。 Contextを使用してReactアプリのテーマを切り替えるThemeTogglerコンポーネントを構築します。

まず、Reactアプリを初期化します。 (私はcreate-react-appを使用することを好みますが、好きな方法を使用できます。)

Reactプロジェクトを初期化したら、 /srcフォルダーにThemeContext.jsというファイルを作成します。 必要に応じて、 /contextというフォルダーを作成し、そこにThemeContextファイルを配置することもできます。

それでは、次に進みましょう。

コンテキストAPIの作成

ThemeContext.jsファイルにテーマコンテキストを作成します。

コンテキストを作成するには、コンテキストオブジェクトを作成するReact.createContextを使用します。 React.createContextへの引数として何でも渡すことができます。 この場合、現在のテーマモードである文字列を渡します。 そのため、現在のテーマモードは「ライト」テーマモードです。

 import React from "react"; const ThemeContext = React.createContext("light"); export default ThemeContext;

このコンテキストをすべてのReactコンポーネントで利用できるようにするには、プロバイダーを使用する必要があります。 プロバイダーとは何ですか? Reactのドキュメントによると、すべてのコンテキストオブジェクトには、消費するコンポーネントがコンテキストの変更をサブスクライブできるようにするProviderReactコンポーネントが付属しています。 コンテキストを他のコンポーネントで使用できるようにするのはプロバイダーです。 そうは言っても、プロバイダーを作成しましょう。

App.jsファイルに移動します。 プロバイダーを作成するには、 ThemeContextをインポートする必要があります。

ThemeContextがインポートされたら、 AppコンポーネントのコンテンツをThemeContext.Providerタグで囲み、 ThemeContext.Providerコンポーネントに、コンポーネントツリーで使用できるようにするデータを含むvalueというプロパティを指定する必要があります。

 function App() { const theme = "light"; return ( <ThemeContext.Provider value = {theme}> <div> </div> </ThemeContext.Provider> ); }

これで、「光」の値がすべてのコンポーネントで利用できるようになりました(これについてはすぐに説明します)。

テーマファイルの作成

次に、明るいテーマと暗いテーマの両方で異なる色の値を含むテーマファイルを作成します。 /srcフォルダーにColors.jsというファイルを作成します。

Colors.jsで、 AppThemeというオブジェクトを作成します。 このオブジェクトには、テーマの色が含まれます。 完了したら、次のようにAppThemeオブジェクトをエクスポートします。

 const AppTheme = { light: { textColor: "#000", backgroundColor: "#fff" }, dark: { textColor: "#fff", backgroundColor: "#333" } } export default AppTheme;

次に、さまざまなReactコンポーネントの作成を開始します。

Reactコンポーネントの作成

次のコンポーネントを作成しましょう。

  • Header
  • ThemeToggler
  • MainWithClass

Header.jsx

 import React from "react"; import ThemeToggler from "./ThemeToggler"; const headerStyles = { padding: "1rem", display: "flex", justifyContent: "space-between", alignItems: "center" } const Header = () => { return( <header style = {headerStyles}> <h1>Context API</h1> <ThemeToggler /> </header> ); } export default Header;

ThemeToggler.jsx

(今のところ、空のdivを返すだけです。)

 import React from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { return( <div style = {themeTogglerStyle}> </div> ); } export default ThemeToggler;

クラスベースのコンポーネントでコンテキストを消費する

ここでは、 ThemeContextの値を使用します。 すでにご存知かもしれませんが、Reactでコンポーネントを作成する方法は2つあります。関数またはクラスを使用する方法です。 両方のメソッドでコンテキストを使用するプロセスが異なるため、アプリケーションのメインセクションとして機能する2つのコンポーネントMainWithClassMainWithFunctionを作成します。

MainWithClassから始めましょう。

MainWithClass.jsx

ThemeContextAppThemeをインポートする必要があります。 それが完了したら、renderメソッドからJSXを返すクラスを作成します。 今、私たちは自分の文脈を消費しなければなりません。 クラスベースのコンポーネントでこれを行うには、次の2つの方法があります。

  1. 最初のメソッドは、 Class.contextTypeを使用します。

    このメソッドを使用するには、 ThemeContextからクラスのcontextTypeプロパティにコンテキストオブジェクトを割り当てます。 その後、 this.contextを使用してコンテキスト値にアクセスできるようになります。 これは、ライフサイクルメソッドのいずれか、さらにはレンダリングメソッドでも参照できます。

     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context]; return( <main></main> ); } }

    ThemeContextをクラスのcontextTypeプロパティに割り当てた後、現在のテーマオブジェクトをcurrentTheme変数に保存しました。

    次に、 currentTheme変数から色を取得し、それらを使用してマークアップのスタイルを設定します。
     render() { const currentTheme = AppTheme[this.context]; return ( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main>

    それでおしまい! ただし、この方法では、1つのコンテキストのみを使用するように制限されます。
  2. 2番目のメソッドは、コンシューマーの使用を含むThemeContext.Consumerです。 各コンテキストオブジェクトには、クラスベースのコンポーネントで使用できるConsumerReactコンポーネントも付属しています。 コンシューマーコンポーネントは子を関数として受け取り、その関数はReactノードを返します。 現在のコンテキスト値は、引数としてその関数に渡されます。

    次に、 MainWithClassコンポーネントのコードを次のように置き換えます。
     class Main extends Component { constructor() { super(); this.state = { } } render(){ return( <ThemeContext.Consumer> { (theme) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } }

    ご覧のとおり、「theme」というエイリアスを付けたThemeContextの現在の値を使用し、そのテーマモードの色の値を取得して、変数currentThemeに割り当てました。 この方法では、複数のコンシューマーを使用できます。

これらは、クラスベースのコンポーネントでコンテキストを消費する2つの方法です。

機能コンポーネントによるコンテキストの消費

機能コンポーネントでコンテキストを消費することは、クラスベースのコンポーネントで消費するよりも簡単で面倒ではありません。 機能コンポーネントでコンテキストを使用するには、 useContextというフックを使用します。

機能コンポーネントでThemeContextを使用すると、次のようになります。

 const Main = () => { const theme = useContext(ThemeContext); const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

ご覧のとおり、私たちがしなければならなかったのは、引数として渡されたThemeContextを使用してuseContextフックを使用することだけでした。

結果を表示するには、App.jsファイルでこれらのさまざまなコンポーネントを使用する必要があります。

ThemeTogglerコンポーネントを使用してテーマを更新する

次に、 ThemeTogglerコンポーネントに取り組みます。 明るいテーマと暗いテーマを切り替えることができる必要があります。 これを行うには、 ThemeContext.jsを編集する必要があります。 React.createContextは、 useStateフックの結果に似たオブジェクトを引数として取ります。

 const ThemeContext = React.createContext(["light", () => {}]);

配列をReact.createContext関数に渡しました。 配列の最初の要素は現在のテーマモードであり、2番目の要素はテーマを更新するために使用される関数です。 すでに述べたように、これはuseState useStateの結果とは異なります。

次に、 App.jsファイルを編集します。 プロバイダーに渡される値をuseStateフックに変更する必要があります。 これで、テーマコンテキストの値はuseStateフックになり、デフォルト値は「light」になります。

 function App() { const themeHook = useState("light"); return ( <ThemeContext.Provider value = {themeHook}> <div> <Header /> <Main /> </div> </ThemeContext.Provider> ); }

ThemeTogglerコンポーネントの作成

ここで、実際にThemeTogglerコンポーネントを作成しましょう。

 import React,{useContext} from "react"; import ThemeContext from "../Context/ThemeContext"; const themeTogglerStyle = { cursor: "pointer" } const ThemeToggler = () => { const[themeMode, setThemeMode] = useContext(ThemeContext); return( <div style = {themeTogglerStyle} onClick = {() => {setThemeMode(themeMode === "light"? "dark": "light")}}> <span title = "switch theme"> {themeMode === "light" ? "" : "️"} </span> </div> ); } export default ThemeToggler;

テーマコンテキストの値は、 useContextを呼び出すたびにフックになるため、配列を返します。 デストラクチャリングを使用して、配列から要素を取得することができました。 次に、 ThemeToggleronClickイベントハンドラーを作成しました。 そのコードを使用すると、テーマトグルをクリックするたびに、アプリケーションのテーマが切り替わります。

次に、 Mainコンポーネントのさまざまなバージョンを編集します。

MainWithClassコンポーネントの編集

  1. Class.contextTypeメソッドを使用するMainWithClassコンポーネントのバージョン:
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component{ constructor(){ super(); } static contextType = ThemeContext; render(){ const currentTheme = AppTheme[this.context[0]]; return( <main style={{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } }
  2. ThemeContext.Consumerメソッドを使用するMainWithClassコンポーネントのバージョン:
     import React, { Component } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; class Main extends Component { constructor() { super(); this.state = {} } render() { return ( <ThemeContext.Consumer> { ([theme]) => { const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ) } } </ThemeContext.Consumer> ); } } export default Main;

MainWithFunctionコンポーネントの編集

MainWithFunctionコンポーネントは、次のように編集する必要があります。

 import React, { useContext } from "react"; import ThemeContext from "../Context/ThemeContext"; import AppTheme from "../Colors"; const Main = () => { const theme = useContext(ThemeContext)[0]; const currentTheme = AppTheme[theme]; return( <main style = {{ padding: "1rem", backgroundColor: `${currentTheme.backgroundColor}`, color: `${currentTheme.textColor}`, }}> <h1>Heading 1</h1> <p>This is a paragraph</p> <button> This is a button</button> </main> ); } export default Main;

結論

それでおしまい! Context APIを使用して、Reactアプリに2つのテーマモードを実装することに成功しました。

その過程で、私たちは次のことを学びました。

  • コンテキストAPIとは何か、そしてそれが解決する問題。
  • コンテキストAPIを使用する場合。
  • Contextを作成し、機能コンポーネントとクラスベースコンポーネントの両方で使用します。

SmashingMagの詳細

  • 最新のWebアプリでのスタイリング
  • IonicとReactを使用したモバイルアプリの構築
  • Webpackとワークボックスを使用してPWAを構築する
  • MutationObserverAPIを理解する