Reactコンテキストでユーザー状態を管理する方法
序章
Reactでは、データが小道具(親コンポーネントから子コンポーネント)を介して上から下に流れるのが一般的ですが、これは常に理想的ではありません。 さまざまな深さの多くのネストされたコンポーネントで利用可能である必要があるデータがあり、小道具を渡すことは維持が難しく、エラーが発生しやすい状況があります。
この状況では、 Redux を使用して状態を管理することをお勧めしますが、多くの人がこれを最初のオプションにすべきではないと主張しています。
React Context は、すべてのレベルで小道具を手動で渡す必要なしに、コンポーネント間でデータを共有するための代替ソリューションです。
この記事では、Context APIを調べ、それを使用してユーザーの状態を管理する方法を学習します。
前提条件
この記事をフォローするには、次のものが必要です。
このチュートリアルは、ノードv15.3.0、npm
v6.14.9、およびreact
v17.0.1で検証されました。
問題を理解する
user
およびavatarSize
プロップを渡すPage
コンポーネントの例を次に示します。
<Page user={user} avatarSize={avatarSize} />
PageLayout
コンポーネントをレンダリングします:
<PageLayout user={user} avatarSize={avatarSize} />
NavigationBar
コンポーネントをレンダリングします:
<NavigationBar user={user} avatarSize={avatarSize} />
user
およびavatarSize
プロップを使用するLink
およびAvatar
をレンダリングします。
<Link href={user.permalink}>
<Avatar user={user} size={avatarSize} />
</Link>
上記の例では、Avatar
コンポーネントのみが実際にuser
プロップを使用しています。 ただし、その祖先コンポーネント(親、祖父母など)のそれぞれは、user
を受け取り、それを渡します。 これは、Avatar
コンポーネントが将来別の小道具を必要とする場合、その祖先コンポーネントのそれぞれがそれを受け取り、渡すことを確認する必要があることを意味します。
実際には、user
状態は多くの異なるコンポーネント間で共有する必要があるため、小道具として渡すと、上記の例よりもさらに深くネストされます。
React.createContext
を作成しています
React.createContext
メソッドは、Context
オブジェクトを返します。 このContext
オブジェクトには、データのサブスクライブを可能にする2つの重要なReactコンポーネントProvider
とConsumer
が付属しています。
Reactは、このContext
オブジェクトにサブスクライブするコンポーネントをレンダリングすると、ツリー内でその上にある最も一致するProvider
コンポーネントから現在のコンテキスト値を読み取ります。
createContext
メソッドは、オプションのdefaultValue
引数を取ります。これは、一致するContext.Provider
コンポーネントがツリーに見つからなかった場合にContext.Consumer
に提供されます。
Context
オブジェクトを作成し、他のコンポーネントで使用できるようにエクスポートします。
import React from 'react';
const userContext = React.createContext({user: {}});
export { userContext };
上記の例では、userContext
を初期化し、{user: {}}
のdefaultValue
を提供しました。
Context
オブジェクトができたので、それに値を指定して変更をサブスクライブできます。
Context.Provider
を適用する
ユーザー状態を値としてcontext.Provider
に渡し、context.Consumer
で使用できるようにします。
import React from 'react';
import Main from './Main';
import {userContext} from './userContext';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {}
};
}
componentDidMount() {
// get and set currently logged in user to state
}
render() {
return (
<userContext.Provider value={this.state.user}>
<Main/>
</userContext.Provider>
);
}
}
export default App;
これは最初から素晴らしいことです。Main
コンポーネントをuserContext.Provider
でラップすると、渡した値をMain
の子孫コンポーネント内で確実に使用できるようになります。
Context.Consumer
を適用する
userContext.Consumer
は子として機能を取ります。 この関数は、現在のコンテキスト値(userContext.Provider
に小道具として渡される値)を受け取り、Reactノードを返します。
import Sidebar from './Sidebar';
import Content from './Content';
import Avatar from './Avatar';
import {userContext} from './userContext';
function Main(props) {
return (
<Sidebar/>
<userContext.Consumer>
{({value}) => {
<Avatar value={value}/>
}}
</userContext.Consumer>
<Content/>
)
}
export default Main;
この場合、App
コンポーネントのstate.user
を値として受け取り、Avatar
コンポーネントをレンダリングします。
ネストされたコンポーネントからのコンテキストの更新
これまで、React Contextを使用して、小道具を手動で渡すことなく、データを必要とするコンポーネントにデータを渡してきました。 次に、ネストされた子コンポーネントからコンテキストを更新できる必要があります。
ユーザーをログアウトするためのボタンを考えてみましょう。
この場合、同じコンテキストを介して関数を渡し、コンシューマーがコンテキストを更新できるようにすることができます。
import React from 'react';
import Main from './Main';
import {userContext} from './userContext';
class App extends React.Component {
constructor(props) {
super(props);
this.state = {
user: {}
};
this.logout = this.logout.bind(this);
}
logout() {
this.setState({user: {}});
}
componentDidMount() {
// get and set currently logged in user to state
}
render() {
const value = {
user: this.state.user,
logoutUser: this.logout
}
return (
<userContext.Provider value={value}>
<Main/>
</userContext.Provider>
);
}
}
export default App;
コンテキストを更新するために渡される関数は、userContext.Provider
コンポーネント内のネストされたコンポーネントで使用できます。
import Sidebar from './Sidebar';
import Content from './Content';
import Avatar from './Avatar';
import LogoutButton from './LogoutButton';
import {userContext} from './userContext';
function Main(props) {
return (
<Sidebar/>
<userContext.Consumer>
{({user, logoutUser}) => {
return (
<Avatar user={user}/>
<LogoutButton onClick={logoutUser}/>
);
}}
</userContext.Consumer>
<Content/>
)
}
export default Main;
Main
コンポーネントにログアウトボタンが追加されました。
これで、ReactのコンテキストAPIを使用してユーザーの状態を管理する方法の例は終わりです。
結論
この記事では、React Contextを紹介し、Context.Producer
とContext.Consumer
を使用しました。
Reactの詳細については、 React.js シリーズのコーディング方法をご覧になるか、Reactトピックページで演習やプログラミングプロジェクトを確認してください。