こんにちは👋🏻、 この記事は、複数のコンポーネント間で状態を管理するためのより効果的な方法を知りたい初心者向けに特別に作成されています。また、コードの保守と理解を困難にする可能性のある、prop ドリルの一般的な問題に対処することも目的としています。まず、コンテキスト API がどのような問題を解決するかから始めましょう。 ビデオ形式の方がお好みの場合は、私の YouTube チャンネルで視聴できるチュートリアルがあります。👇🏻 https://youtu.be/4p_VWn1AEvg?embedable=true プロップドリリングとは何ですか? 親コンポーネントから子コンポーネントにデータを渡す必要がある場合があり、その間の多数のコンポーネントを介して props を渡すことになることがあります。これは と呼ばれ、すぐに面倒なことになります。これを明確にするために、例を見てみましょう。 prop ドリリング 図に示すように、アプリケーションのルートにある コンポーネントでデータを取得したとします。ここで、深くネストされたコンポーネント、たとえば コンポーネントがこのデータにアクセスする必要がある場合、通常は、データが に到達する前に、 と コンポーネントを介して props として渡されます。これは、アプリが大きくなるにつれて厄介な問題になる可能性があります。 App Grandchild Grandchild Parent Child もう一つの視覚的表現を次に示します。 上記の例では、 コンポーネントにはユーザー データが必要ですが、このデータは、中間コンポーネント自体がデータを使用しない場合でも、まず コンポーネントと コンポーネントを通過する必要があります。では、これをどのようにクリーンアップすればよいでしょうか。ここで、Context API が役立ちます。 Profile App Navigation プロップドリル: コンポーネントの再レンダリングを増加 定型コードの増加 コンポーネントの依存関係を作成する パフォーマンスが低下する React コンテキスト API React.js の Context API を使用すると、コンポーネント ツリーの各レベルにデータをプロパティとして渡すことなく、コンポーネント間でデータを渡すことができます。これは、コンテキスト オブジェクトで状態を定義するグローバル状態管理システムのように機能し、コンポーネント ツリーのどこからでも簡単にアクセスできます。例を使ってこれを理解しましょう。 図からわかるように、複数のコンポーネントがアクセスするデータを格納するコンテキスト オブジェクトがあります。このデータは、API またはサードパーティのサービスから取得されます。任意のコンポーネントでこのコンテキスト データにアクセスする前に、このデータを必要とするすべてのコンポーネントをコンテキスト プロバイダー コンポーネントにラップする必要があります。 ナビゲーション コンポーネントとプロファイル コンポーネントのデータにのみアクセスする必要がある場合は、アプリ コンポーネントをラップする必要はありません。関連するコンポーネントを でラップすると、コンテキスト データを使用する任意のコンポーネントでコンテキスト データに直接アクセスできます。まだ理解できなくても心配しないでください。コードを調べて、実際に動作を確認しましょう。 ContextProvider コンテキスト API の使用方法 まず、 を使用して React アプリを作成しましょう。以下のコマンドをコピーしてプロジェクトをセットアップするだけです。 Vite.js npm create vite@latest プロジェクト名を追加 反応を選択 オプションからTypescriptを選択 cd project_name // to change to project directory npm install npm run dev 次に、ブラウザで開発サーバー を開くことができます。 http://localhost:5173 まず、必要なフォルダーを作成しましょう。これがプロジェクトのフォルダー構造です。 src | components | context コンポーネント フォルダーに ファイルを作成し、次のコードを追加します。 Profile.jsx import React from 'react' const Profile = () => { return ( <div>Profile</div> ) } export default Profile コンポーネント フォルダーに というコンポーネントをもう 1 つ作成します。 Navbar.jsx import Profile from './Profile' const Navbar = () => { return ( <nav style={{ display: "flex", justifyContent: "space-between", alignItems: "center", width: "90%", height: "10vh", backgroundColor: theme === "light" ? "#fff" : "#1b1b1b", color: theme === "light" ? "#1b1b1b" : "#fff", border: "1px solid #fff", borderRadius: "5px", padding: "0 20px", marginTop: "40px", }}> <h1>LOGO</h1> <Profile /> </nav> ) } export default Navbar この コンポーネントを ファイルにインポートしましょう。 <Navbar /> App.jsx import Navbar from "./components/Navbar"; function App() { return ( <main style={{ display: "flex", flexDirection: "column", justifyContent: "start", alignItems: "center", height: "100vh", width: "100vw", }} > <Navbar /> </main> ); } export default App; つまり、基本的に、 コンポーネントは の子であり、 コンポーネントの子です。 <Profile /> <Navbar /> <Navbar /> <App /> コンテキストAPIの追加 フォルダーに ファイルを作成しましょう。ファイルに次のコードを追加します。 context UserContext.jsx import { createContext, useEffect, useState } from "react"; export const UserContext = createContext(); export const UserProvider = ({ children }) => { const [user, setUser] = useState(null); const fetchUserData = async (id) => { const response = await fetch( `https://jsonplaceholder.typicode.com/users/${id}` ).then((response) => response.json()); console.log(response); setUser(response); }; useEffect(() => { fetchUserData(1); }, []); return ( <UserContext.Provider value={{ user, fetchUserData }} > {children} </UserContext.Provider> ); }; まず、 を使用して空の オブジェクトを作成します。 からインポートするようにしてください。コンテキスト オブジェクト内にデフォルト値を追加できますが、今のところは null のままにしておきます。 createContext UserContext react 次に、 のように、 を使用してプロバイダーを返す を作成します。これは子コンポーネントをラップし、値には子コンポーネントで使用したいものを何でも渡すことができます。 UserContext.Provider UserContext UserProvider 現在、ユーザー データを取得するために を使用しています。jsonplaceholder 、テスト目的で偽の API エンドポイントを提供します。fetchUserData は を受け入れ、その ID を使用してユーザー データを取得します。次に、応答を 状態に保存します。 jsonplaceholder API は fetchUserData id user で 関数を呼び出しているので、ページの読み込み時に関数が呼び出され、 状態にデータが挿入されます。 useEffect fetchUserData user ここで、このコンテキストを コンポーネントで使用してみましょう。 を使用して コンポーネントをラップします。次のコードと同じです。 <App /> <UserProvider /> <NavBar /> <UserProvider> <Navbar /> </UserProvider> コンポーネントで 状態を使用しましょう。そのためには、 フックを使用します。これは を受け取り、 状態や 関数など、 で渡した値を提供します。 コンポーネントは既にプロバイダーでラップされている コンポーネント内にあるため、ラップする必要がないことに注意してください。 <Profile /> user useContext UserContext user fetchUserData UserProvider <Profile /> <Navbar /> を開き、次のコードを追加します。 Profile.jsx const { user } = useContext(UserContext); if (user) { return ( <span style={{ fontWeight: "bold", }} > {user.name} </span> ); } else { return <span>Login</span>; } ここでは、 の 状態を使用しています。 がいる場合はユーザー名を表示し、そうでない場合はログイン メッセージのみを表示します。出力を見ると、navbar コンポーネントにユーザー名があるはずです。これは、任意のコンポーネントのコンテキストにある任意の状態を直接使用する方法です。この状態を使用するコンポーネントは 内にラップする必要があります。 UserContext user user <Provider /> 複数のコンテキストを使用することもできます。次の例に示すように、プロバイダー コンポーネントを別のプロバイダー コンポーネント内にラップするだけです。 <ThemeProvider> <UserProvider> <Navbar /> </UserProvider> </ThemeProvider> 上記の例では、テーマの状態を管理する を使用しています。 <ThemeProvider /> 複数のコンテキスト プロバイダーを使用する完全な例については、上記の YouTube ビデオをご覧ください。 React Context API での再レンダリングの最適化 複数のコンポーネントで Context API を使用する場合に発生する問題が 1 つあります。Context API で状態または値が変更されるたびに、すべてのコンポーネントが変更された状態を使用していない場合でも、その特定のコンテキストにサブスクライブされているすべてのコンポーネントが再レンダリングされます。この再レンダリングの問題を理解するために、コンテキストを使用してカウント値を保存および表示する コンポーネントを作成しましょう。 <Counter /> 次の例を確認してください。コンポーネント フォルダーに ファイルを作成し、次のコードを貼り付けることができます。 Counter.jsx import { createContext, memo, useContext, useState } from "react"; const CountContext = createContext(); const CountProvider = ({ children }) => { const [count, setCount] = useState(0); return ( <CountContext.Provider value={{ count, setCount }}> {children} </CountContext.Provider> ); }; function CountTitle() { console.log("This is Count Title component"); return <h1>Counter Title</h1>; } function CountDisplay() { console.log("This is CountDisplay component"); const { count } = useContext(CountContext); return <div>Count: {count}</div>; } function CounterButton() { console.log("This is CounterButton component"); const { count, setCount } = useContext(CountContext); return ( <> <CountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </> ); } export default function Counter() { return ( <CountProvider> <CounterButton /> </CountProvider> ); } 上記のコードでは: まず、createContext を使用して 1 つの オブジェクトを作成します CountContext createContext. カウント値を格納するための状態が 1 つあります。count と メソッド value プロパティを通じて子コンポーネントに送信しています。 CountProvider, setCount count 個々のコンポーネントが何回再レンダリングされるかを確認するために、コンポーネントを個別に作成しました。 : このコンポーネントはタイトルのみを表示し、コンテキストからの値も使用しません。 <CountTitle /> : このコンポーネントはカウント値を表示し、コンテキストからの 状態を使用します。 <CountDisplay /> count : このコンポーネントは、上記のコンポーネントと、 を使用してカウント値を増やすボタンの両方をレンダリングします。 <CounterButton /> setCount 最後に、他のコンポーネントがカウント値にアクセスできるように、 コンポーネントを コンポーネント内にラップします。 <CounterButton /> CountProvider ここで、コードを実行して ボタンをクリックすると、状態が変化するたびにすべてのコンポーネントが再レンダリングされていることがログに表示されます。 は count 値を使用していないにもかかわらず、再レンダリングされています。これは、 の親コンポーネントである が count の値を使用して更新しているために発生し、再レンダリングされるのです。 Increase <CountTitle /> <CountTitle /> <CounterButton /> この動作を最適化するにはどうすればよいでしょうか。答えは です。React の 使用すると、プロパティが変更されていない場合にコンポーネントの再レンダリングをスキップできます。 コンポーネントの後に、次の行を追加しましょう。 memo memo <CountTitle /> const MemoizedCountTitle = React.memo(CountTitle) ここで、 コンポーネントで コンポーネントをレンダリングしているところで、次のコードのように を に置き換えます。 <CounterButton /> <CountTitle /> <CountTitle /> <MemoizedCountTitle /> <> <MemoizedCountTitle /> <CountDisplay /> <button onClick={() => setCount(count + 1)}>Increase</button> </> ここで、カウントを増やしてログを確認すると、 コンポーネントがレンダリングされなくなっていることがわかります。 <CountTitle /> Redux と Context API 、より予測可能な状態遷移を伴う複雑な状態管理のための状態管理ライブラリです。一方、Context API は、プロパティ ドリリングなしでコンポーネント ツリーを介してデータを渡すために、シンプルな状態管理用に設計されています。では、どちらを選択すればよいのでしょうか。 Redux 状態が頻繁に変化しない場合は、 を使用して、シンプルでローカライズされた状態管理を行います。 React Context API 複雑な状態管理のニーズには Redux を使用します。特に、構造化された状態管理の利点が追加のセットアップを上回る大規模なアプリケーションでは を使用します。 Redux 状態管理の一般的な選択肢としてもう 1 つライブラリがあります。それは 。 React Recoil です 、Context API のシンプルさと Redux のパワーおよびパフォーマンスを提供することを目的とした、React の状態管理ライブラリです。 React Recoil は についてさらに詳しく知りたい場合は、コメントでお知らせください。皆さんのフィードバックに基づいて、このトピックに関する詳細なチュートリアルを作成します。 React Recoil 結論 Context API は、複数のコンポーネントにまたがる状態を管理するための強力かつ効率的な方法を提供し、prop ドリルの問題に効果的に対処します。Context API を使用すると、コードを簡素化し、不要な再レンダリングを減らし、アプリケーション全体のパフォーマンスを向上させることができます。 React.js Context API は単純な状態管理に最適ですが、より複雑なアプリケーションでは や などの他の状態管理ライブラリを使用すると効果的です。これらのツールをいつどのように使用するかを理解することで、より保守性と拡張性に優れた React アプリケーションを構築できるようになります。 Redux React Recoil この記事を読んでいただきありがとうございます。お役に立てれば幸いです。React、Redux、Next.js を使用したプロジェクトの学習と構築に興味がある場合は、こちらの YouTube チャンネルをご覧ください: CodeBucks 他にも読んでみたい記事がこちらにあります: Lenis と GSAP を使用して Next.js でスムーズスクロールを実装する方法 React.js でユーザーの位置情報を取得する方法 試してみるべき人気の VS Code テーマ トップ 10 私の個人ブログをご覧ください: DevDreaming