ReactのコンテキストAPIの紹介
公開: 2022-03-10このチュートリアルでは、フックを正しく理解している必要があります。 それでも、始める前に、それらが何であるか、そしてこの記事で使用するフックについて簡単に説明します。
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
を使用して、 blue
とred
のテーマを切り替えることができます。
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つのコンポーネントMainWithClass
とMainWithFunction
を作成します。
MainWithClass
から始めましょう。
MainWithClass.jsx
ThemeContext
とAppTheme
をインポートする必要があります。 それが完了したら、renderメソッドからJSXを返すクラスを作成します。 今、私たちは自分の文脈を消費しなければなりません。 クラスベースのコンポーネントでこれを行うには、次の2つの方法があります。
- 最初のメソッドは、
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番目のメソッドは、コンシューマーの使用を含む
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
を呼び出すたびにフックになるため、配列を返します。 デストラクチャリングを使用して、配列から要素を取得することができました。 次に、 ThemeToggler
のonClick
イベントハンドラーを作成しました。 そのコードを使用すると、テーマトグルをクリックするたびに、アプリケーションのテーマが切り替わります。
次に、 Main
コンポーネントのさまざまなバージョンを編集します。
MainWithClass
コンポーネントの編集
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> ); } }
-
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を理解する