開発者ドキュメント

コンテキストを使用してReactコンポーネント間で状態を共有する方法

著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。

序章

このチュートリアルでは、 Reactコンテキストを使用して、複数のコンポーネント間で状態を共有します。 Reactコンテキストは、データを props として明示的に渡すことなく、他のコンポーネントと情報を共有するためのインターフェースです。 これは、親コンポーネントと深くネストされた子コンポーネント間で情報を共有したり、サイト全体のデータを1つの場所に保存して、アプリケーションのどこからでもアクセスできることを意味します。 データとともに更新機能を提供することにより、ネストされたコンポーネントからデータを更新することもできます。

Reactコンテキストは、プロジェクトの集中状態管理システムとして使用するのに十分な柔軟性があります。または、アプリケーションのより小さなセクションにスコープを設定することもできます。 コンテキストを使用すると、追加のサードパーティツールを使用せずに、少量の構成でアプリケーション全体でデータを共有できます。 これにより、 Redux のようなツールに代わる軽量のツールが提供されます。これは、大規模なアプリケーションには役立ちますが、中規模のプロジェクトではセットアップが多すぎる可能性があります。

このチュートリアル全体を通して、コンテキストを使用して、さまざまなコンポーネント間で共通のデータセットを使用するアプリケーションを構築します。 これを説明するために、ユーザーがカスタムサラダを作成できるWebサイトを作成します。 Webサイトは、コンテキストを使用して、顧客情報、お気に入りのアイテム、およびカスタムサラダを保存します。 次に、そのデータにアクセスし、小道具を介してデータを渡すことなく、アプリケーション全体でデータを更新します。 このチュートリアルの終わりまでに、コンテキストを使用してプロジェクトのさまざまなレベルでデータを格納する方法と、ネストされたコンポーネントのデータにアクセスして更新する方法を学習します。

前提条件

ステップ1—アプリケーションの基盤を構築する

このステップでは、カスタムサラダビルダーの一般的な構造を構築します。 可能なトッピング、選択したトッピングのリスト、および顧客情報を表示するコンポーネントを作成します。 静的データを使用してアプリケーションを構築すると、さまざまなコンポーネントでさまざまな情報がどのように使用されているか、およびコンテキストで役立つデータを特定する方法がわかります。

作成するアプリケーションの例を次に示します。

コンポーネント間で使用する必要のある情報があることに注意してください。 たとえば、ユーザー名(このサンプルでは Kwame )は、ナビゲーション領域にユーザーデータを表示しますが、お気に入りのアイテムを識別するため、またはチェックアウトページ用にユーザー情報が必要になる場合もあります。 ユーザー情報は、アプリケーション内の任意のコンポーネントからアクセスできる必要があります。 サラダビルダー自体を見ると、各サラダ材料は画面下部の Your Salad リストを更新できる必要があるため、そのデータを次の場所から保存して更新する必要があります。各コンポーネントにもアクセスできます。

アプリの構造を理解できるように、すべてのデータをハードコーディングすることから始めます。 後で、次のステップからコンテキストを追加します。 アプリケーションが成長し始めると、コンテキストが最大の価値を提供するため、このステップでは、コンポーネントツリー全体でコンテキストがどのように機能するかを示すために、いくつかのコンポーネントを構築します。 小さなコンポーネントやライブラリの場合、多くの場合、ラッピングコンポーネントと、 ReactHooksクラスベースの管理などの低レベルの状態管理手法を使用できます。

複数のコンポーネントを含む小さなアプリを作成しているため、 JSS をインストールして、クラス名の競合が発生しないようにし、コンポーネントと同じファイルにスタイルを追加できるようにします。 JSSの詳細については、Reactコンポーネントのスタイリングを参照してください。

次のコマンドを実行します。

  1. npm install react-jss

npmはコンポーネントをインストールし、完了すると次のようなメッセージが表示されます。

Output
+ react-jss@10.3.0 added 27 packages from 10 contributors, removed 10 packages andaudited 1973 packages in 15.507s

JSSがインストールされたので、必要なさまざまなコンポーネントを検討します。 ページの上部に、 Navigation ウェルカムメッセージを格納するコンポーネント。 次のコンポーネントは SaladMaker 自体。 これは、ビルダーと下部の YourSaladリストとともにタイトルを保持します。 材料のあるセクションは、 SaladBuilder、内部にネスト SaladMaker. 各成分は、のインスタンスになります SaladItem 成分。 最後に、一番下のリストはというコンポーネントになります SaladSummary.

注:コンポーネントをこのように分割する必要はありません。 アプリケーションで作業するとき、機能を追加するにつれて構造が変化し、進化します。 この例は、コンテキストがツリー内のさまざまなコンポーネントにどのように影響するかを調べるための構造を提供することを目的としています。

必要なコンポーネントがわかったので、それぞれのディレクトリを作成します。

  1. mkdir src/components/Navigation
  2. mkdir src/components/SaladMaker
  3. mkdir src/components/SaladItem
  4. mkdir src/components/SaladBuilder
  5. mkdir src/components/SaladSummary

次に、コンポーネントを上から下に構築します。 Navigation. まず、コンポーネントファイルをテキストエディタで開きます。

  1. nano src/components/Navigation/Navigation.js

と呼ばれるコンポーネントを作成します Navigation スタイリングを追加して Navigation 境界線とパディング:

state-context-tutorial / src / components / Navigation / Navigation.js
import React from 'react';
import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
  wrapper: {
    borderBottom: 'black solid 1px',
    padding: [15, 10],
    textAlign: 'right',
  }
});

export default function Navigation() {
  const classes = useStyles();
  return(
    <div className={classes.wrapper}>
      Welcome, Kwame
    </div>
  )
}

JSSを使用しているため、CSSファイルではなく、コンポーネントで直接スタイルオブジェクトを作成できます。 ラッパー div パディングがあり、 solid black 境界線を描き、テキストを右に揃えます textAlign.

ファイルを保存して閉じます。 次に、開く App.js、プロジェクトのルートです:

  1. nano src/components/App/App.js

をインポートします Navigation コンポーネントを作成し、強調表示された行を追加して空のタグ内にレンダリングします。

state-context-tutorial / src / components / App / App.js
import React from 'react';
import Navigation from '../Navigation/Navigation';

function App() {
  return (
    <>
      <Navigation />
    </>
  );
}

export default App;

ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、ナビゲーションバーが表示されます。

ナビゲーションバーはグローバルコンポーネントと考えてください。この例では、ナビゲーションバーがすべてのページで再利用されるテンプレートコンポーネントとして機能しているためです。

次のコンポーネントは SaladMaker 自体。 これは、特定のページまたは特定の状態でのみレンダリングされるコンポーネントです。

開ける SaladMaker.js テキストエディタで:

  1. nano src/components/SaladMaker/SaladMaker.js

次のようなコンポーネントを作成します <h1> 見出しのタグ:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react';
import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export default function SaladMaker() {
  const classes = useStyles();
  return(
    <>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
    </>
  )
}

このコードでは、 textAlign コンポーネントをページの中央に配置します。 The rolearia-label の属性 span elementは、 Accessible Rich Internet Applications(ARIA)を使用したアクセシビリティに役立ちます。

ファイルを保存して閉じます。 開ける App.js コンポーネントをレンダリングするには:

  1. nano src/components/App/App.js

輸入 SaladMaker 後にレンダリングします Navigation 成分:

state-context-tutorial / src / components / App / App.js
import React from 'react';
import Navigation from '../Navigation/Navigation';
import SaladMaker from '../SaladMaker/SaladMaker';

function App() {
  return (
    <>
      <Navigation />
      <SaladMaker />
    </>
  );
}

export default App;

ファイルを保存して閉じます。 これを行うと、ページが再読み込みされ、見出しが表示されます。

次に、というコンポーネントを作成します SaladItem. これは、個々の材料ごとのカードになります。

テキストエディタでファイルを開きます。

  1. nano src/components/SaladItem/SaladItem.js

このコンポーネントは、アイテムの名前、アイテムがユーザーのお気に入りかどうかを示すアイコン、クリックするとサラダにアイテムを追加するボタン内に配置された絵文字の3つの部分で構成されます。 次の行をに追加します SaladItem.js:

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React from 'react';
import PropTypes from 'prop-types';
import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
  add: {
    background: 'none',
    border: 'none',
    cursor: 'pointer',
  },
  favorite: {
    fontSize: 20,
    position: 'absolute',
    top: 10,
    right: 10,
  },
  image: {
    fontSize: 80
  },
  wrapper: {
    border: 'lightgrey solid 1px',
    margin: 20,
    padding: 25,
    position: 'relative',
    textAlign: 'center',
    textTransform: 'capitalize',
    width: 200,
  }
});

export default function SaladItem({ image, name }) {
  const classes = useStyles();
  const favorite = true;
  return(
    <div className={classes.wrapper}>
        <h3>
          {name}
        </h3>
        <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>
          {favorite ? '😋' : ''}
        </span>
        <button className={classes.add}>
          <span className={classes.image} role="img" aria-label={name}>{image}</span>
        </button>
    </div>
  )
}

SaladItem.propTypes = {
  image: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
}

The imagename 小道具になります。 コードは favorite 変数および三項演算子は、条件付きで favorite アイコンが表示されるかどうか。 The favorite 変数は、後でユーザーのプロファイルの一部としてコンテキストを使用して決定されます。 今のところ、 true. スタイリングにより、お気に入りのアイコンがカードの右上隅に配置され、ボタンのデフォルトの境界線と背景が削除されます。 The wrapper クラスは小さな境界線を追加し、テキストの一部を変換します。 最後に、 PropTypes は、弱いタイピングシステムを追加して、間違ったプロップタイプが渡されないようにするための強制を提供します。

ファイルを保存して閉じます。 次に、さまざまなアイテムをレンダリングする必要があります。 あなたはと呼ばれるコンポーネントでそれを行います SaladBuilder、一連のに変換されるアイテムのリストが含まれます SaladItem コンポーネント:

開ける SaladBuilder:

  1. nano src/components/SaladBuilder/SaladBuilder.js

これが本番アプリの場合、このデータは多くの場合、アプリケーションプログラミングインターフェイス(API)から取得されます。 しかし今のところ、成分のハードコードされたリストを使用してください:

state-context-tutorial / src / components / SaladBuilder / SaladBuilder.js
import React from 'react';
import SaladItem from '../SaladItem/SaladItem';

import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
  wrapper: {
    display: 'flex',
    flexWrap: 'wrap',
    padding: [10, 50],
    justifyContent: 'center',
  }
});

const ingredients = [
  {
    image: '🍎',
    name: 'apple',
  },
  {
    image: '🥑',
    name: 'avocado',
  },
  {
    image: '🥦',
    name: 'broccoli',
  },
  {
    image: '🥕',
    name: 'carrot',
  },
  {
    image: '🍷',
    name: 'red wine dressing',
  },
  {
    image: '🍚',
    name: 'seasoned rice',
  },
];

export default function SaladBuilder() {
  const classes = useStyles();
  return(
    <div className={classes.wrapper}>
      {
        ingredients.map(ingredient => (
          <SaladItem
            key={ingredient.name}
            image={ingredient.image}
            name={ingredient.name}
          />
        ))
      }
    </div>
  )
}

このスニペットは、 map()配列メソッドを使用して、リスト内の各アイテムにマップし、 nameimage の小道具として SaladItem 成分。 マップするときは、必ず各アイテムにキーを追加してください。 このコンポーネントのスタイリングにより、次の表示が追加されます。 flex flexboxレイアウトの場合、コンポーネントをラップして中央に配置します。

ファイルを保存して閉じます。

最後に、コンポーネントをでレンダリングします SaladMaker そのため、ページに表示されます。

開ける SaladMaker:

  1. nano src/components/SaladMaker/SaladMaker.js

次にインポート SaladBuilder 見出しの後にレンダリングします。

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react';
import { createUseStyles } from 'react-jss';
import SaladBuilder from '../SaladBuilder/SaladBuilder';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export default function SaladMaker() {
  const classes = useStyles();
  return(
    <>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
      <SaladBuilder />
    </>
  )
}

ファイルを保存して閉じます。 これを行うと、ページがリロードされ、コンテンツが見つかります。

最後のステップは、進行中のサラダの要約を追加することです。 このコンポーネントは、ユーザーが選択したアイテムのリストを表示します。 今のところ、アイテムをハードコーディングします。 手順3でコンテキストを使用して更新します。

開ける SaladSummary テキストエディタで:

  1. nano src/components/SaladSummary/SaladSummary.js

コンポーネントは、見出しと並べ替えられていないアイテムのリストになります。 フレックスボックスを使用して、それらをラップさせます。

state-context-tutorial / src / components / SaladSummary / SaladSummary.jss
import React from 'react';
import { createUseStyles } from 'react-jss';

const useStyles = createUseStyles({
  list: {
    display: 'flex',
    flexDirection: 'column',
    flexWrap: 'wrap',
    maxHeight: 50,
    '& li': {
      width: 100
    }
  },
  wrapper: {
    borderTop: 'black solid 1px',
    display: 'flex',
    padding: 25,
  }
});

export default function SaladSummary() {
  const classes = useStyles();
  return(
    <div className={classes.wrapper}>
      <h2>Your Salad</h2>
      <ul className={classes.list}>
        <li>Apple</li>
        <li>Avocado</li>
        <li>Carrots</li>
      </ul>
    </div>
  )
}

ファイルを保存します。 次に開きます SaladMaker アイテムをレンダリングするには:

  1. nano src/components/SaladMaker/SaladMaker.js

インポートして追加 SaladSummary 後に SaladBuilder:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React from 'react';
import { createUseStyles } from 'react-jss';
import SaladBuilder from '../SaladBuilder/SaladBuilder';
import SaladSummary from '../SaladSummary/SaladSummary';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export default function SaladMaker() {
  const classes = useStyles();
  return(
    <>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
      <SaladBuilder />
      <SaladSummary />
    </>
  )
}

ファイルを保存して閉じます。 これを行うと、ページが更新され、完全なアプリケーションが見つかります。

アプリケーション全体で共有データがあります。 The Navigation コンポーネントと SaladItem コンポーネントは両方とも、ユーザーについて何かを知る必要があります。名前とお気に入りのリストです。 The SaladItem また、でアクセス可能なデータを更新する必要があります SaladSummary 成分。 コンポーネントは共通の祖先を共有しますが、ツリーを介してデータを渡すことは困難であり、エラーが発生しやすくなります。

そこで、コンテキストが登場します。 共通の親でデータを宣言し、コンポーネントの階層に明示的に渡すことなく、後でアクセスできます。

このステップでは、ユーザーがオプションのリストからサラダを作成できるようにするアプリケーションを作成しました。 他のコンポーネントによって制御されるデータにアクセスまたは更新する必要があるコンポーネントのセットを作成しました。 次のステップでは、コンテキストを使用してデータを保存し、子コンポーネントにアクセスします。

ステップ2—ルートコンポーネントからのデータの提供

このステップでは、コンテキストを使用して、コンポーネントのルートに顧客情報を保存します。 カスタムコンテキストを作成してから、プロジェクトのルートに情報を格納するProviderと呼ばれる特別なラッピングコンポーネントを使用します。 次に、 useContext Hook を使用して、ネストされたコンポーネントでプロバイダーに接続し、静的情報を表示できるようにします。 このステップを完了すると、情報の集中ストアを提供し、さまざまなコンポーネントのコンテキストに格納されている情報を使用できるようになります。

最も基本的なコンテキストは、情報を共有するためのインターフェイスです。 多くのアプリケーションには、ユーザー設定、テーマ情報、サイト全体のアプリケーション変更など、アプリケーション全体で共有する必要のあるユニバーサル情報がいくつかあります。 コンテキストを使用すると、その情報をルートレベルで保存し、どこからでもアクセスできます。 親に情報を設定するので、それは常に利用可能であり、常に最新であることがわかります。

コンテキストを追加するには、という新しいディレクトリを作成します User:

  1. mkdir src/components/User

User これは、コンポーネントとしても、と呼ばれる特別なフックのデータの一部としても使用するという点で、従来のコンポーネントにはなりません。 useContext. 今のところ、フラットなファイル構造を維持しますが、多くのコンテキストを使用する場合は、それらを別のディレクトリ構造に移動する価値があるかもしれません。

次に、開きます User.js テキストエディタで:

  1. nano src/components/User/User.js

ファイル内で、 createContext Reactから関数を実行し、関数を実行して結果をエクスポートします。

state-context-tutorial / src / components / User / User.js
import { createContext } from 'react';

const UserContext = createContext();
export default UserContext;

関数を実行することにより、コンテキストを登録しました。 結果、 UserContextは、コンポーネントで使用するものです。

ファイルを保存して閉じます。

次のステップは、コンテキストを要素のセットに適用することです。 これを行うには、と呼ばれるコンポーネントを使用します Provider. The Provider は、データを設定してから、いくつかの子コンポーネントをラップするコンポーネントです。 ラップされた子コンポーネントは、からのデータにアクセスできます。 Provider とともに useContext 針。

ユーザーデータはプロジェクト全体で一定であるため、コンポーネントツリーのできるだけ高い位置に配置します。 このアプリケーションでは、ルートレベルに配置します App 成分:

開く App:

  1. nano src/components/App/App.js

次の強調表示されたコード行を追加して、コンテキストをインポートし、データを渡します。

state-context-tutorial / src / components / App / App.js
import React from 'react';
import Navigation from '../Navigation/Navigation';
import SaladMaker from '../SaladMaker/SaladMaker';
import UserContext from '../User/User';

const user = {
  name: 'Kwame',
  favorites: [
    'avocado',
    'carrot'
  ]
}

function App() {
  return (
    <UserContext.Provider value={user}>
      <Navigation />
      <SaladMaker />
    </UserContext.Provider>
  );
}

export default App;

一般的なアプリケーションでは、ユーザーデータをフェッチするか、サーバー側のレンダリング中にデータを保存します。 この場合、APIから受け取る可能性のあるいくつかのデータをハードコーディングしました。 と呼ばれるオブジェクトを作成しました user これは、ユーザー名を文字列として保持し、お気に入りの材料の配列を保持します。

次に、 UserContext、その後ラップ NavigationSaladMaker と呼ばれるコンポーネントで UserContext.Provider. この場合の方法に注意してください UserContext 標準のReactコンポーネントとして機能しています。 このコンポーネントは、と呼ばれる単一の小道具を取ります value. その小道具はあなたが共有したいデータになります、この場合は user 物体。

ファイルを保存して閉じます。 これで、データはアプリケーション全体で利用できるようになります。 ただし、データを使用するには、コンテキストをもう一度インポートしてアクセスする必要があります。

コンテキストを設定したので、コンポーネント内のハードコードされたデータを動的な値に置き換えることができます。 でハードコードされた名前を置き換えることから始めます Navigation 設定したユーザーデータを使用 UserContext.Provider.

開ける Navigation.js:

  1. nano src/components/Navigation/Navigation.js

の中に Navigation、インポート useContext Reactからのフックと UserContext コンポーネントディレクトリから。 次に電話 useContext を使用して UserContext 引数として。 とは異なり UserContext.Provider、レンダリングする必要はありません UserContext JSXで。 フックは、で提供したデータを返します value 小道具。 データをという新しい変数に保存します user、を含むオブジェクトです namefavorites. 次に、ハードコードされた名前を次のように置き換えることができます user.name:

state-context-tutorial / src / components / Navigation / Navigation.js
import React, { useContext } from 'react';
import { createUseStyles } from 'react-jss';

import UserContext from '../User/User';

const useStyles = createUseStyles({
  wrapper: {
    outline: 'black solid 1px',
    padding: [15, 10],
    textAlign: 'right',
  }
});

export default function Navigation() {
  const user = useContext(UserContext);
  const classes = useStyles();
  return(
    <div className={classes.wrapper}>
      Welcome, {user.name}
    </div>
  )
}

UserContext のコンポーネントとして働いた App.js、しかしここでは、それをデータの一部としてより多く使用しています。 ただし、必要に応じて、コンポーネントとして機能することもできます。 を使用して同じデータにアクセスできます Consumer それはの一部です UserContext. 追加することでデータを取得します UserContext.Consumer JSXにアクセスし、関数を子として使用してデータにアクセスします。

使用することは可能ですが Consumer コンポーネント、フックを使用すると、同じ最新情報を提供しながら、多くの場合、短くて読みやすくなります。 これが、このチュートリアルでフックアプローチを使用する理由です。

ファイルを保存して閉じます。 これを行うと、ページが更新され、同じ名前が表示されます。 しかし、今回は動的に更新されました。

この場合、データは多くのコンポーネント間を移動しませんでした。 データが移動したパスを表すコンポーネントツリーは、次のようになります。

| UserContext.Provider
  | Navigation

このユーザー名を小道具として渡すことができ、この規模では効果的な戦略になる可能性があります。 しかし、アプリケーションが成長するにつれて、 Navigation コンポーネントが移動します。 と呼ばれるコンポーネントがあるかもしれません Header それは Navigation コンポーネントと、 TitleBar、または多分あなたは作成します Template コンポーネントをネストしてから、 Navigation そこで。 コンテキストを使用することで、リファクタリングする必要がなくなります Navigation 限り Provider はツリーの上位にあり、リファクタリングが容易になります。

ユーザーデータを必要とする次のコンポーネントは SaladItem 成分。 の中に SaladItem コンポーネントには、ユーザーのお気に入りの配列が必要です。 材料がユーザーのお気に入りである場合は、条件付きで絵文字を表示します。

開ける SaladItem.js:

  1. nano src/components/SaladItem/SaladItem.js

輸入 useContextUserContext、次に電話 useContextUserContext. その後、材料が中にあるかどうかを確認してください favorites を使用した配列 includes 方法:

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React, { useContext } from 'react';
import PropTypes from 'prop-types';
import { createUseStyles } from 'react-jss';

import UserContext from '../User/User';

const useStyles = createUseStyles({
...
});

export default function SaladItem({ image, name }) {
  const classes = useStyles();
  const user = useContext(UserContext);
  const favorite = user.favorites.includes(name);
  return(
    <div className={classes.wrapper}>
        <h3>
          {name}
        </h3>
        <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>
          {favorite ? '😋' : ''}
        </span>
        <button className={classes.add}>
          <span className={classes.image} role="img" aria-label={name}>{image}</span>
        </button>
    </div>
  )
}

SaladItem.propTypes = {
  image: PropTypes.string.isRequired,
  name: PropTypes.string.isRequired,
}

ファイルを保存して閉じます。 すると、ブラウザが更新され、お気に入りのアイテムだけに絵文字が表示されます。

ようではない Navigation、コンテキストははるかに遠くまで進んでいます。 コンポーネントツリーは次のようになります。

| User.Provider
  | SaladMaker
    | SaladBuilder
      | SaladItem

情報は、小道具なしで2つの中間コンポーネントをスキップしました。 ツリー全体でデータをプロップとして渡す必要がある場合、それは多くの作業になり、将来の開発者がコードをリファクタリングして、プロップを渡すのを忘れるリスクがあります。 状況に応じて、アプリケーションの成長と進化に合わせてコードが機能することを確信できます。

このステップでは、コンテキストを作成し、 Provider コンポーネントツリーにデータを設定します。 また、コンテキストにアクセスしました useContext 複数のコンポーネントにまたがるフックと使用コンテキスト。 このデータは静的であるため、初期設定後に変更されることはありませんが、データを共有したり、複数のコンポーネント間でデータを変更したりする必要がある場合があります。 次のステップでは、コンテキストを使用してネストされたデータを更新します。

ステップ3—ネストされたコンポーネントからのデータの更新

このステップでは、コンテキストと useReducer ネストされたコンポーネントが消費および更新できる動的データを作成するためのフック。 更新します SaladItem データを設定するコンポーネント SaladSummary 使用して表示します。 また、ルートコンポーネントの外部にコンテキストプロバイダーを設定します。 この手順を完了すると、複数のコンポーネント間でデータを使用および更新できるアプリケーションが作成され、アプリケーションのさまざまなレベルで複数のコンテキストプロバイダーを追加できるようになります。

この時点で、アプリケーションは複数のコンポーネントにわたってユーザーデータを表示していますが、ユーザーとの対話はありません。 前の手順では、コンテキストを使用して単一のデータを共有しましたが、関数を含むデータのコレクションを共有することもできます。 つまり、データを共有したり、データを更新する機能を共有したりすることができます。

アプリケーションでは、それぞれ SaladItem 共有リストを更新する必要があります。 その後、あなたの SaladSummary コンポーネントは、ユーザーが選択したアイテムを表示し、リストに追加します。 問題は、これらのコンポーネントが直接の子孫ではないため、データと更新機能を小道具として渡すことができないことです。 しかし、彼らは共通の親を共有しています: SaladMaker.

コンテキストとReduxなどの他の状態管理ソリューションとの大きな違いの1つは、コンテキストが中央ストアになることを意図していないことです。 アプリケーション全体で複数回使用し、ルートレベルまたはコンポーネントツリーの奥深くで開始できます。 つまり、競合を心配することなく、アプリケーション全体にコンテキストを広げて、焦点を絞ったデータコレクションを作成できます。

コンテキストに焦点を合わせ続けるには、 Providers 可能な場合は、最も近い共有の親をラップします。 この場合、それは、に別のコンテキストを追加するのではなく、 App、コンテキストを追加します SaladMaker 成分。

開ける SaladMaker:

  1. nano src/components/SaladMaker/SaladMaker.js

次に、という新しいコンテキストを作成してエクスポートします SaladContext:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { createContext } from 'react';
import { createUseStyles } from 'react-jss';
import SaladBuilder from '../SaladBuilder/SaladBuilder';
import SaladSummary from '../SaladSummary/SaladSummary';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export const SaladContext = createContext();

export default function SaladMaker() {
  const classes = useStyles();
  return(
    <>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
      <SaladBuilder />
      <SaladSummary />
    </>
  )
}

前の手順では、コンテキスト用に別のコンポーネントを作成しましたが、この場合は、使用しているのと同じファイルにコンポーネントを作成しています。 以来 User に直接関連していないようです App、それらを分離しておく方が理にかなっているかもしれません。 ただし、 SaladContext と密接に結びついています SaladMaker コンポーネントを一緒に保持すると、より読みやすいコードが作成されます。

さらに、次のようなより一般的なコンテキストを作成できます OrderContext、複数のコンポーネント間で再利用できます。 その場合は、別のコンポーネントを作成する必要があります。 今のところ、それらを一緒に保ちます。 別のパターンに移行する場合は、後でいつでもリファクタリングできます。

追加する前に Provider 共有したいデータについて考えてください。 アイテムの配列とアイテムを追加するための関数が必要になります。 他の一元化された状態管理ツールとは異なり、コンテキストはデータの更新を処理しません。 後で使用するためのデータを保持するだけです。 データを更新するには、フックなどの他の状態管理ツールを使用する必要があります。 同じコンポーネントのデータを収集する場合は、次のいずれかを使用します。 useState また useReducer フック。 これらのフックを初めて使用する場合は、Reactコンポーネントのフックを使用して状態を管理する方法を確認してください。

The useReducer すべてのアクションで最新の状態を更新する必要があるため、フックが適しています。

作成する reducer 新しいアイテムをに追加する関数 state 配列、次に使用します useReducer を作成するためのフック salad 配列と setSalad 関数:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { useReducer, createContext } from 'react';
import { createUseStyles } from 'react-jss';
import SaladBuilder from '../SaladBuilder/SaladBuilder';
import SaladSummary from '../SaladSummary/SaladSummary';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export const SaladContext = createContext();

function reducer(state, item) {
  return [...state, item]
}

export default function SaladMaker() {
  const classes = useStyles();
  const [salad, setSalad] = useReducer(reducer, []);
  return(
    <>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
      <SaladBuilder />
      <SaladSummary />
    </>
  )
}

これで、を含むコンポーネントができました。 salad 共有したいデータ、という関数 setSalad データを更新するには、 SaladContext 同じコンポーネントでデータを共有します。 この時点で、それらを組み合わせる必要があります。

組み合わせるには、を作成する必要があります Provider. 問題は、 Provider シングルを取る value 小道具として。 合格できないので saladsetSalad 個別に、それらをオブジェクトに結合し、オブジェクトをとして渡す必要があります value:

state-context-tutorial / src / components / SaladMaker / SaladMaker.js
import React, { useReducer, createContext } from 'react';
import { createUseStyles } from 'react-jss';
import SaladBuilder from '../SaladBuilder/SaladBuilder';
import SaladSummary from '../SaladSummary/SaladSummary';

const useStyles = createUseStyles({
  wrapper: {
    textAlign: 'center',
  }
});

export const SaladContext = createContext();

function reducer(state, item) {
  return [...state, item]
}

export default function SaladMaker() {
  const classes = useStyles();
  const [salad, setSalad] = useReducer(reducer, []);
  return(
    <SaladContext.Provider value={{ salad, setSalad }}>
      <h1 className={classes.wrapper}>
        <span role="img" aria-label="salad">🥗 </span>
          Build Your Custom Salad!
          <span role="img" aria-label="salad"> 🥗</span>
      </h1>
      <SaladBuilder />
      <SaladSummary />
    </SaladContext.Provider>
  )
}

ファイルを保存して閉じます。 と同じように Navigation、コンテキストを作成する必要がないように見える場合があります SaladSummary コンテキストと同じコンポーネントにあります。 通過 salad 小道具は完全に合理的ですが、後でリファクタリングすることになるかもしれません。 ここでコンテキストを使用すると、情報が1か所にまとめられます。

次に、 SaladItem コンポーネントとプル setSalad コンテキスト外で機能します。

コンポーネントをテキストエディタで開きます。

  1. nano src/components/SaladItem/SaladItem.js

中身 SaladItem、からコンテキストをインポートします SaladMaker、次に引き出します setSalad 破壊を使用して機能します。 クリックイベントをボタンに追加して、 setSalad 関数。 ユーザーがアイテムを複数回追加できるようにするため、アイテムごとに一意のIDを作成して、 map 関数は一意の割り当てが可能になります key:

state-context-tutorial / src / components / SaladItem / SaladItem.js
import React, { useReducer, useContext } from 'react';
import PropTypes from 'prop-types';
import { createUseStyles } from 'react-jss';

import UserContext from '../User/User';
import { SaladContext } from '../SaladMaker/SaladMaker';

const useStyles = createUseStyles({
...
});

const reducer = key => key + 1;
export default function SaladItem({ image, name }) {
  const classes = useStyles();
  const { setSalad } = useContext(SaladContext)
  const user = useContext(UserContext);
  const favorite = user.favorites.includes(name);
  const [id, updateId] = useReducer(reducer, 0);
  function update() {
    setSalad({
      name,
      id: `${name}-${id}`
    })
    updateId();
  };
  return(
    <div className={classes.wrapper}>
        <h3>
          {name}
        </h3>
        <span className={classes.favorite} aria-label={favorite ? 'Favorite' : 'Not Favorite'}>
          {favorite ? '😋' : ''}
        </span>
        <button className={classes.add} onClick={update}>
          <span className={classes.image} role="img" aria-label={name}>{image}</span>
        </button>
    </div>
  )
}
...

一意のIDを作成するには、 useReducer クリックするたびに値をインクリメントするためのフック。 最初のクリックでは、IDは次のようになります 0; 2番目は 1、 等々。 この値をユーザーに表示することはありません。 これにより、後でマッピング関数の一意の値が作成されます。

一意のIDを作成した後、次の関数を作成しました update IDをインクリメントして呼び出す setSalad. 最後に、関数をボタンにアタッチしました。 onClick 小道具。

ファイルを保存して閉じます。 最後のステップは、のコンテキストから動的データをプルすることです。 SaladSummary.

開ける SaladSummary:

  1. nano src/components/SaladSummary/SaladSummary.js

をインポートします SaladContext コンポーネント、次に引き出します salad destructuringを使用したデータ。 ハードコードされたリストアイテムを、マップする関数に置き換えます salad、オブジェクトをに変換する <li> 要素。 必ず使用してください id として key:

state-context-tutorial / src / components / SaladSummary / SaladSummary.js
import React, { useContext } from 'react';
import { createUseStyles } from 'react-jss';

import { SaladContext } from '../SaladMaker/SaladMaker';

const useStyles = createUseStyles({
...
});

export default function SaladSummary() {
  const classes = useStyles();
  const { salad } = useContext(SaladContext);
  return(
    <div className={classes.wrapper}>
      <h2>Your Salad</h2>
      <ul className={classes.list}>
        {salad.map(({ name, id }) => (<li key={id}>{name}</li>))}
      </ul>
    </div>
  )
}

ファイルを保存して閉じます。 そうすると、アイテムをクリックできるようになり、概要が更新されます。

コンテキストによって、さまざまなコンポーネントでデータを共有および更新する機能がどのように提供されたかに注目してください。 コンテキストはアイテム自体を更新しませんでしたが、それはあなたに使用する方法を与えました useReducer 複数のコンポーネントに接続します。 さらに、ツリーの下位にコンテキストを配置する自由もありました。 コンテキストを常にルートに保持するのが最善のように思えるかもしれませんが、コンテキストを低く保つことで、中央ストアに未使用の状態が残ることを心配する必要がなくなります。 コンポーネントをアンマウントするとすぐに、データは消えます。 データを保存したい場合は問題になる可能性がありますが、その場合は、コンテキストを上位の親に上げる必要があります。

アプリケーションツリーの下位にあるコンテキストを使用するもう1つの利点は、競合を気にせずにコンテキストを再利用できることです。 サンドイッチメーカーとサラダメーカーを備えたより大きなアプリがあるとします。 と呼ばれる一般的なコンテキストを作成できます OrderContext そうすれば、データや名前の競合を気にすることなく、コンポーネントの複数のポイントで使用できます。 あなたが持っていた場合 SaladMakerSandwichMaker、ツリーは次のようになります。

| App
  | Salads
    | OrderContext
      | SaladMaker
  | Sandwiches
    | OrderContext
      | SandwichMaker

注意してください OrderContext 二度あります。 それは大丈夫です、 useContext フックは最寄りのプロバイダーを探します。

このステップでは、コンテキストを使用してデータを共有および更新しました。 また、ルート要素の外側にコンテキストを配置して、ルートコンポーネントを乱雑にすることなく、情報を必要とするコンポーネントの近くに配置しました。 最後に、コンテキストと状態管理フックを組み合わせて、動的で複数のコンポーネント間でアクセス可能なデータを作成しました。

結論

コンテキストは、アプリケーション全体でデータを保存および使用する機能を提供する強力で柔軟なツールです。 追加のサードパーティによるインストールや構成を必要としない組み込みツールを使用して、分散データを処理する機能を提供します。

再利用可能なコンテキストを作成することは、要素間でデータにアクセスする必要があるフォームや、タブと表示の両方に共通のコンテキストを必要とするタブビューなど、さまざまな共通コンポーネントで重要です。 テーマ、フォームデータ、アラートメッセージなど、さまざまな種類の情報をコンテキストに保存できます。 コンテキストを使用すると、中間コンポーネントを介してデータを渡す方法や、ストアを大きくしすぎずに中央ストアにデータを保存する方法を気にせずに、データにアクセスできるコンポーネントを自由に構築できます。

Reactのチュートリアルをもっと見たい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。

モバイルバージョンを終了