ウェブサイト検索

状態を管理するためのReactコンテキストの使い方


Reactのコンテクストは、繰り返しのプロップドリリングを排除するのに役立つ機能です。プロップはしばしば親コンポーネントから深く入れ子になった子コンポーネントに渡されます。これには、各中間コンポーネントがツリー内の次のコンポーネントにプロップを「転送」する必要があります。

コンテキストを使用すると、ツリーを通じてプロップを手動で各レベルに転送することなく渡すことができます。これは、トップレベルのアプリケーション状態を表すデータに特に便利です。ユーザー設定、認証トークン、およびAPIレスポンスからキャッシュされたデータは、Context APIの良い候補です。

このデータは、しばしばReduxのような専用の状態ストアによって管理されます。代わりに、Reactのコンテキストを使用することもできます。これにより、Reduxがアプリケーションレベルの状態を保持するだけの場合のアプリ内の複雑さが大幅に削減されます。アクション、ディスパッチャー、およびリデューサーを作成する代わりに、Reactのコンテキストを使用することができます。

文脈のない例

ユーザーがログインした後、アプリの状態にユーザーの名前やメールアドレスなどの重要なデータを保存したくなることがよくあります。これにより、各コンポーネントはAPIサーバーへの往復を行うことなく、ユーザーに関する情報を表示できます。

これを実装するための素朴な方法は次のとおりです:

const UserProfileLink = ({user}) => {

return (

<div>

<p>Logged in as {user.Name}</p>

<a href="/logout">Logout</a>

</div>

);

};

const Header = ({user}) => {

return (

<div>

<h1>My App</h1>

<UserProfileLink user={user} />

</div>

);

}

const App = () => {

const [user, setUser] = useState({

Name: "Foo Bar",

Email: "foobar@example.com"

});

return <Header user={user} />

}

アプリには3つのコンポーネントがあります。最上位の

App

コンポーネントは、その状態内で現在のユーザーを追跡します。それは以下をレンダリングします

Header

コンポーネント。

Header

レンダリング

UserProfileLink

ユーザーの名前を表示します。

重要なのは、ただ

UserProfileLink

実際にユーザーオブジェクトと対話します。

Header

まだ受け取らなければならない

user

プロパティですが。これはそのままに転送されます。

UserProfileLink

消費されることなく

Header

このアプローチの問題は、コンポーネントツリーが複雑になるにつれて悪化します。中間のコンポーネントが自分自身でプロプスを使用しなくても、複数のネストされたレベルを通じてプロプスを渡すことになるかもしれません。

コンテキストAPIの追加

これらの問題は、Contexts APIを使用して軽減できます。上記の例をリファクタリングして削除することができます

user

プロップから

Header

その

App

コンポーネントは現在のユーザーオブジェクトを保存するための新しいコンテキストを作成します。

UserProfileLink

コンテキストを消費します。コンポーネントは、コンテキストによって提供されるユーザーデータにアクセスできるようになります。これは、コンポーネントをReduxストアに接続するのと似た概念です。

const UserContext = React.createContext(null);

const UserProfileLink = () => {

const user = useContext(UserContext);

return (

<div>

<p>Logged in as {user.Name}</p>

<a href="/logout">Logout</a>

</div>

);

};

const Header = () => {

return (

<div>

<h1>My App</h1>

<UserProfileLink />

</div>

);

}

const App = () => {

const [user, setUser] = useState({

Name: "Foo Bar",

Email: "foobar@example.com"

});

return (

<UserContext.Provider value={user}>

<Header />

</UserContext>

);

}

このリファクタリングされたコンポーネントのセットは、コンテキストの使い方を示しています。

そのプロセスは、利用可能にしたい値の新しいコンテキストを作成することから始まります。呼び出しは

React.createContext(null)

デフォルト値を持つ新しいコンテキストを設定します

null

その

App

コンポーネントはラップするように書き直されました

Header

の中で

UserContext.Provider

その

value

プロパティはコンテキストの現在の値を決定します。これは次のように設定されます

user

状態に保持されているオブジェクト。コンテキストプロバイダーの下にネストされたコンポーネントは、今やコンテキストを消費し、ユーザーオブジェクトにアクセスできるようになります。

プロバイダーインスタンスの中にネストされていないコンポーネントからコンテキストにアクセスしようとすると、そのコンポーネントは渡したデフォルト値を受け取ります。

createContext()

これは一般的に避けるべきですが、決して変わらない静的なコンテキスト値については例外です。

提供されたコンテキスト値へのアクセスが観察されます

UserProfileLink

その

user

プロパティが削除されました。このコンポーネントは、ReactのuseContext()フックを使用して、現在の値を取得します。

UserContext

. これにより、ユーザーオブジェクトが提供されます。

App

コンポーネント!

最終的な変更は

Header

コンポーネント。これ以上転送する必要はありません。

user

それを完全に取り除くことができるプロップ。実際、

user

小道具はアプリ全体から消えました。現在は次のように供給されています。

App

中へ

UserContext

プロバイダー、特定のコンポーネントではありません。

クラスコンポーネントでのコンテキストの使用

これまでのところ、私たちは関数コンポーネント内のコンテキストのみを使用してきました。コンテキストはここでうまく機能します。

useContext()

フックは子コンポーネント内で提供されたデータにアクセスすることを簡素化します。

クラスコンポーネントでもコンテキストを使用できます。推奨される方法は、staticを設定することです。

contextType

使用したいコンテキストインスタンスへのクラスプロパティ。Reactはこのプロパティを読み取り、設定します。

context

コンポーネントインスタンスのプロパティを、コンテキストによって提供された現在の値に設定します。

const UserContext = React.createContext({Name: "Foo Bar"});

class MyComponent extends React.Component {

// Tell React to inject the `UserContext` value

static contextType = UserContext;

render() {

// Requested context value made available as `this.context`

return <p>{this.context.Name}</p>;

}

}

別のアプローチは、コンポーネントの子要素をコンテキストの「コンシューマー」の中でレンダリングすることです。各コンテキストのコンシューマーにアクセスすることができます。

Consumer

コンテキストインスタンスのプロパティ。

コンシューマーの子供として関数を提供する必要があります。関数はコンポーネントがレンダーされるときにコンテキストの値で呼び出されます。

これがどのように見えるかです:

const UserContext = React.createContext({Name: "Foo Bar"});

class MyComponent extends React.Component {

render() {

return (

<UserContext.Consumer>

{user => <p>{user.Name}</p>}

</UserContext.Consumer>

);

}

}

使用する

contextType

各コンポーネントに対して1つのコンテキストに制限します。コンテキスト消費者はこの問題に対処しますが、あなたのコンポーネントのレンダーメソッドをより不透明にする可能性があります。どちらのアプローチも、まったく簡単ではありません。

useContext()

関数コンポーネントに利用可能なフック。

コンテキスト値の更新

コンテキストの値は、プロップスと同様に機能します。子コンポーネントがコンテキストの値を更新する必要がある場合は、関数をコンテキストに追加します。子コンポーネントは、その関数を呼び出してコンテキストの値の変更を実行できます。

const UserContext = React.createContext(null);

const UserProfileLink = () => {

const context = useContext(UserContext);

return (

<div>

<p>Logged in as {context.user.Name}</p>

<a onClick={() => context.logoutUser()}>Logout</a>

</div>

);

};

const Header = () => {

return (

<div>

<h1>My App</h1>

<UserProfileLink />

</div>

);

}

const App = () => {

const [user, setUser] = useState({

Name: "Foo Bar",

Email: "foobar@example.com"

});

const contextValue = {

user,

logoutUser: () => setUser(null)

}

return (

<UserContext.Provider value={contextValue}>

<Header />

</UserContext>

);

}

この改訂された例はどのように示していますか

App

今や提供されています

logoutUser

コンテキスト内の関数。コンテキストの利用者は、この関数を呼び出して更新できます。

user

中で

App

コンポーネントの状態が変更され、それに応じてコンテキストの値が修正されます。

再レンダリングの管理

Reactはプロバイダーのすべての子コンポーネントを再レンダリングします。

value

プロパティの変更。値の変更は使用して比較されます。

Object.is()

つまり、コンテキストプロバイダーとしてオブジェクトを使用する際には注意が必要です。

value
const App = () => {

return (

<ExampleContext.Provider value={{foo: "bar"}}>

<NestedComponent />

</ExampleContext.Provider>

);

}

このコンポーネントは再レンダリングされます

NestedComponent

毎回

App

レンダリング。プロバイダーのために新しいオブジェクトインスタンスが作成されます。

value

各タイムごとにpropを変更することで、Reactはコンポーネントの子要素を再レンダリングします。

これを持ち上げることで対処できます。

value

オブジェクトをコンポーネントの状態に変換します。これにより、毎回同じオブジェクトがレンダリングされることが保証されます。

const App = () => {

const [obj, setObj] = useState({foo: "bar"});

return (

<ExampleContext.Provider value={obj}>

<NestedComponent />

</ExampleContext.Provider>

);

}

要約

Reactのコンテキストは、データをコンポーネントツリーに渡すための一級の方法を提供することで、プロップのドリリングを回避するのに役立ちます。コンテキストは、Reduxのような状態管理ライブラリの良い代替手段です。それらはReactに組み込まれており、Reduxが深くネストされたコンポーネントを共有状態源に接続するためだけに使用されるプロジェクトの間で注目を集めています。

コンテキストは、アプリケーション内で保持される「グローバル」データを意図しています。また、アプリの特定のサブセクションのデータを保持することもできます。コンテキストは、ローカルなコンポーネントの状態を完全に置き換えることを目的としていません。個々のコンポーネントは、データが浅くネストされたツリーを上下に渡るだけの場合には、状態やプロパティを引き続き使用するべきです。

関連記事: