コンテキストを使用してReactコンポーネント間で状態を共有する方法
著者は、 Creative Commons を選択して、 Write forDOnationsプログラムの一環として寄付を受け取りました。
序章
このチュートリアルでは、 Reactコンテキストを使用して、複数のコンポーネント間で状態を共有します。 Reactコンテキストは、データを props として明示的に渡すことなく、他のコンポーネントと情報を共有するためのインターフェースです。 これは、親コンポーネントと深くネストされた子コンポーネント間で情報を共有したり、サイト全体のデータを1つの場所に保存して、アプリケーションのどこからでもアクセスできることを意味します。 データとともに更新機能を提供することにより、ネストされたコンポーネントからデータを更新することもできます。
Reactコンテキストは、プロジェクトの集中状態管理システムとして使用するのに十分な柔軟性があります。または、アプリケーションのより小さなセクションにスコープを設定することもできます。 コンテキストを使用すると、追加のサードパーティツールを使用せずに、少量の構成でアプリケーション全体でデータを共有できます。 これにより、 Redux のようなツールに代わる軽量のツールが提供されます。これは、大規模なアプリケーションには役立ちますが、中規模のプロジェクトではセットアップが多すぎる可能性があります。
このチュートリアル全体を通して、コンテキストを使用して、さまざまなコンポーネント間で共通のデータセットを使用するアプリケーションを構築します。 これを説明するために、ユーザーがカスタムサラダを作成できるWebサイトを作成します。 Webサイトは、コンテキストを使用して、顧客情報、お気に入りのアイテム、およびカスタムサラダを保存します。 次に、そのデータにアクセスし、小道具を介してデータを渡すことなく、アプリケーション全体でデータを更新します。 このチュートリアルの終わりまでに、コンテキストを使用してプロジェクトのさまざまなレベルでデータを格納する方法と、ネストされたコンポーネントのデータにアクセスして更新する方法を学習します。
前提条件
-
Node.jsを実行する開発環境が必要になります。 このチュートリアルは、Node.jsバージョン10.20.1およびnpmバージョン6.14.4でテストされました。 これをmacOSまたはUbuntu18.04にインストールするには、Node.jsをインストールしてmacOSにローカル開発環境を作成する方法またはの
PPAを使用したインストール ]セクションの手順に従います。 Ubuntu18.04にNode.jsをインストールする方法。 -
Create React App でセットアップされたReact開発環境で、不要なボイラープレートが削除されています。 これを設定するには、ステップ1 —Reactクラスコンポーネントの状態を管理する方法のチュートリアルの空のプロジェクトを作成します。 このチュートリアルでは、
state-context-tutorial
プロジェクト名として。 -
また、 JavaScriptでコーディングする方法にあるJavaScriptの基本的な知識と、HTMLおよびCSSの基本的な知識も必要です。 HTMLとCSSの便利なリソースは、 Mozilla DeveloperNetworkです。
-
Reactコンポーネントを使用します。
useState
フック、そしてuseReducer
チュートリアルReactでカスタムコンポーネントを作成する方法およびReactコンポーネントのフックで状態を管理する方法で学ぶことができるフック。
ステップ1—アプリケーションの基盤を構築する
このステップでは、カスタムサラダビルダーの一般的な構造を構築します。 可能なトッピング、選択したトッピングのリスト、および顧客情報を表示するコンポーネントを作成します。 静的データを使用してアプリケーションを構築すると、さまざまなコンポーネントでさまざまな情報がどのように使用されているか、およびコンテキストで役立つデータを特定する方法がわかります。
作成するアプリケーションの例を次に示します。
コンポーネント間で使用する必要のある情報があることに注意してください。 たとえば、ユーザー名(このサンプルでは Kwame )は、ナビゲーション領域にユーザーデータを表示しますが、お気に入りのアイテムを識別するため、またはチェックアウトページ用にユーザー情報が必要になる場合もあります。 ユーザー情報は、アプリケーション内の任意のコンポーネントからアクセスできる必要があります。 サラダビルダー自体を見ると、各サラダ材料は画面下部の Your Salad リストを更新できる必要があるため、そのデータを次の場所から保存して更新する必要があります。各コンポーネントにもアクセスできます。
アプリの構造を理解できるように、すべてのデータをハードコーディングすることから始めます。 後で、次のステップからコンテキストを追加します。 アプリケーションが成長し始めると、コンテキストが最大の価値を提供するため、このステップでは、コンポーネントツリー全体でコンテキストがどのように機能するかを示すために、いくつかのコンポーネントを構築します。 小さなコンポーネントやライブラリの場合、多くの場合、ラッピングコンポーネントと、 ReactHooksやクラスベースの管理などの低レベルの状態管理手法を使用できます。
複数のコンポーネントを含む小さなアプリを作成しているため、 JSS をインストールして、クラス名の競合が発生しないようにし、コンポーネントと同じファイルにスタイルを追加できるようにします。 JSSの詳細については、Reactコンポーネントのスタイリングを参照してください。
次のコマンドを実行します。
- 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
.
注:コンポーネントをこのように分割する必要はありません。 アプリケーションで作業するとき、機能を追加するにつれて構造が変化し、進化します。 この例は、コンテキストがツリー内のさまざまなコンポーネントにどのように影響するかを調べるための構造を提供することを目的としています。
必要なコンポーネントがわかったので、それぞれのディレクトリを作成します。
- mkdir src/components/Navigation
- mkdir src/components/SaladMaker
- mkdir src/components/SaladItem
- mkdir src/components/SaladBuilder
- mkdir src/components/SaladSummary
次に、コンポーネントを上から下に構築します。 Navigation
. まず、コンポーネントファイルをテキストエディタで開きます。
- nano src/components/Navigation/Navigation.js
と呼ばれるコンポーネントを作成します Navigation
スタイリングを追加して Navigation
境界線とパディング:
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
、プロジェクトのルートです:
- nano src/components/App/App.js
をインポートします Navigation
コンポーネントを作成し、強調表示された行を追加して空のタグ内にレンダリングします。
import React from 'react';
import Navigation from '../Navigation/Navigation';
function App() {
return (
<>
<Navigation />
</>
);
}
export default App;
ファイルを保存して閉じます。 これを行うと、ブラウザが更新され、ナビゲーションバーが表示されます。
ナビゲーションバーはグローバルコンポーネントと考えてください。この例では、ナビゲーションバーがすべてのページで再利用されるテンプレートコンポーネントとして機能しているためです。
次のコンポーネントは SaladMaker
自体。 これは、特定のページまたは特定の状態でのみレンダリングされるコンポーネントです。
開ける SaladMaker.js
テキストエディタで:
- nano src/components/SaladMaker/SaladMaker.js
次のようなコンポーネントを作成します <h1>
見出しのタグ:
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 role
と aria-label
の属性 span
elementは、 Accessible Rich Internet Applications(ARIA)を使用したアクセシビリティに役立ちます。
ファイルを保存して閉じます。 開ける App.js
コンポーネントをレンダリングするには:
- nano src/components/App/App.js
輸入 SaladMaker
後にレンダリングします Navigation
成分:
import React from 'react';
import Navigation from '../Navigation/Navigation';
import SaladMaker from '../SaladMaker/SaladMaker';
function App() {
return (
<>
<Navigation />
<SaladMaker />
</>
);
}
export default App;
ファイルを保存して閉じます。 これを行うと、ページが再読み込みされ、見出しが表示されます。
次に、というコンポーネントを作成します SaladItem
. これは、個々の材料ごとのカードになります。
テキストエディタでファイルを開きます。
- nano src/components/SaladItem/SaladItem.js
このコンポーネントは、アイテムの名前、アイテムがユーザーのお気に入りかどうかを示すアイコン、クリックするとサラダにアイテムを追加するボタン内に配置された絵文字の3つの部分で構成されます。 次の行をに追加します 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 image
と name
小道具になります。 コードは favorite
変数および三項演算子は、条件付きで favorite
アイコンが表示されるかどうか。 The favorite
変数は、後でユーザーのプロファイルの一部としてコンテキストを使用して決定されます。 今のところ、 true
. スタイリングにより、お気に入りのアイコンがカードの右上隅に配置され、ボタンのデフォルトの境界線と背景が削除されます。 The wrapper
クラスは小さな境界線を追加し、テキストの一部を変換します。 最後に、 PropTypes は、弱いタイピングシステムを追加して、間違ったプロップタイプが渡されないようにするための強制を提供します。
ファイルを保存して閉じます。 次に、さまざまなアイテムをレンダリングする必要があります。 あなたはと呼ばれるコンポーネントでそれを行います SaladBuilder
、一連のに変換されるアイテムのリストが含まれます SaladItem
コンポーネント:
開ける SaladBuilder
:
- nano src/components/SaladBuilder/SaladBuilder.js
これが本番アプリの場合、このデータは多くの場合、アプリケーションプログラミングインターフェイス(API)から取得されます。 しかし今のところ、成分のハードコードされたリストを使用してください:
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()配列メソッドを使用して、リスト内の各アイテムにマップし、 name
と image
の小道具として SaladItem
成分。 マップするときは、必ず各アイテムにキーを追加してください。 このコンポーネントのスタイリングにより、次の表示が追加されます。 flex
flexboxレイアウトの場合、コンポーネントをラップして中央に配置します。
ファイルを保存して閉じます。
最後に、コンポーネントをでレンダリングします SaladMaker
そのため、ページに表示されます。
開ける SaladMaker
:
- nano src/components/SaladMaker/SaladMaker.js
次にインポート SaladBuilder
見出しの後にレンダリングします。
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
テキストエディタで:
- nano src/components/SaladSummary/SaladSummary.js
コンポーネントは、見出しと並べ替えられていないアイテムのリストになります。 フレックスボックスを使用して、それらをラップさせます。
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
アイテムをレンダリングするには:
- nano src/components/SaladMaker/SaladMaker.js
インポートして追加 SaladSummary
後に SaladBuilder
:
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
:
- mkdir src/components/User
User
これは、コンポーネントとしても、と呼ばれる特別なフックのデータの一部としても使用するという点で、従来のコンポーネントにはなりません。 useContext
. 今のところ、フラットなファイル構造を維持しますが、多くのコンテキストを使用する場合は、それらを別のディレクトリ構造に移動する価値があるかもしれません。
次に、開きます User.js
テキストエディタで:
- nano src/components/User/User.js
ファイル内で、 createContext
Reactから関数を実行し、関数を実行して結果をエクスポートします。
import { createContext } from 'react';
const UserContext = createContext();
export default UserContext;
関数を実行することにより、コンテキストを登録しました。 結果、 UserContext
は、コンポーネントで使用するものです。
ファイルを保存して閉じます。
次のステップは、コンテキストを要素のセットに適用することです。 これを行うには、と呼ばれるコンポーネントを使用します Provider
. The Provider
は、データを設定してから、いくつかの子コンポーネントをラップするコンポーネントです。 ラップされた子コンポーネントは、からのデータにアクセスできます。 Provider
とともに useContext
針。
ユーザーデータはプロジェクト全体で一定であるため、コンポーネントツリーのできるだけ高い位置に配置します。 このアプリケーションでは、ルートレベルに配置します App
成分:
開く App
:
- nano 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
、その後ラップ Navigation
と SaladMaker
と呼ばれるコンポーネントで UserContext.Provider
. この場合の方法に注意してください UserContext
標準のReactコンポーネントとして機能しています。 このコンポーネントは、と呼ばれる単一の小道具を取ります value
. その小道具はあなたが共有したいデータになります、この場合は user
物体。
ファイルを保存して閉じます。 これで、データはアプリケーション全体で利用できるようになります。 ただし、データを使用するには、コンテキストをもう一度インポートしてアクセスする必要があります。
コンテキストを設定したので、コンポーネント内のハードコードされたデータを動的な値に置き換えることができます。 でハードコードされた名前を置き換えることから始めます Navigation
設定したユーザーデータを使用 UserContext.Provider
.
開ける Navigation.js
:
- nano src/components/Navigation/Navigation.js
の中に Navigation
、インポート useContext
Reactからのフックと UserContext
コンポーネントディレクトリから。 次に電話 useContext
を使用して UserContext
引数として。 とは異なり UserContext.Provider
、レンダリングする必要はありません UserContext
JSXで。 フックは、で提供したデータを返します value
小道具。 データをという新しい変数に保存します user
、を含むオブジェクトです name
と favorites
. 次に、ハードコードされた名前を次のように置き換えることができます user.name
:
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
:
- nano src/components/SaladItem/SaladItem.js
輸入 useContext
と UserContext
、次に電話 useContext
と UserContext
. その後、材料が中にあるかどうかを確認してください favorites
を使用した配列 includes
方法:
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
:
- nano src/components/SaladMaker/SaladMaker.js
次に、という新しいコンテキストを作成してエクスポートします SaladContext
:
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
関数:
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
小道具として。 合格できないので salad
と setSalad
個別に、それらをオブジェクトに結合し、オブジェクトをとして渡す必要があります value
:
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
コンテキスト外で機能します。
コンポーネントをテキストエディタで開きます。
- nano src/components/SaladItem/SaladItem.js
中身 SaladItem
、からコンテキストをインポートします SaladMaker
、次に引き出します setSalad
破壊を使用して機能します。 クリックイベントをボタンに追加して、 setSalad
関数。 ユーザーがアイテムを複数回追加できるようにするため、アイテムごとに一意のIDを作成して、 map
関数は一意の割り当てが可能になります key
:
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
:
- nano src/components/SaladSummary/SaladSummary.js
をインポートします SaladContext
コンポーネント、次に引き出します salad
destructuringを使用したデータ。 ハードコードされたリストアイテムを、マップする関数に置き換えます salad
、オブジェクトをに変換する <li>
要素。 必ず使用してください id
として key
:
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
そうすれば、データや名前の競合を気にすることなく、コンポーネントの複数のポイントで使用できます。 あなたが持っていた場合 SaladMaker
と SandwichMaker
、ツリーは次のようになります。
| App
| Salads
| OrderContext
| SaladMaker
| Sandwiches
| OrderContext
| SandwichMaker
注意してください OrderContext
二度あります。 それは大丈夫です、 useContext
フックは最寄りのプロバイダーを探します。
このステップでは、コンテキストを使用してデータを共有および更新しました。 また、ルート要素の外側にコンテキストを配置して、ルートコンポーネントを乱雑にすることなく、情報を必要とするコンポーネントの近くに配置しました。 最後に、コンテキストと状態管理フックを組み合わせて、動的で複数のコンポーネント間でアクセス可能なデータを作成しました。
結論
コンテキストは、アプリケーション全体でデータを保存および使用する機能を提供する強力で柔軟なツールです。 追加のサードパーティによるインストールや構成を必要としない組み込みツールを使用して、分散データを処理する機能を提供します。
再利用可能なコンテキストを作成することは、要素間でデータにアクセスする必要があるフォームや、タブと表示の両方に共通のコンテキストを必要とするタブビューなど、さまざまな共通コンポーネントで重要です。 テーマ、フォームデータ、アラートメッセージなど、さまざまな種類の情報をコンテキストに保存できます。 コンテキストを使用すると、中間コンポーネントを介してデータを渡す方法や、ストアを大きくしすぎずに中央ストアにデータを保存する方法を気にせずに、データにアクセスできるコンポーネントを自由に構築できます。
Reactのチュートリアルをもっと見たい場合は、 Reactトピックページを確認するか、React.jsシリーズのコーディング方法ページに戻ってください。