I. 概要
コンテキストは、コンポーネント ツリーのレイヤーを介して明示的にプロップを渡すことなく、コンポーネント間で値を共有する方法を提供します。値の取得と使用のレベルが大きく離れている場合、またはこの値を使用する必要があるコンポーネントが多数あり、分散している場合は、Context を使用してデータを共有し、値を渡すために多数の繰り返し props を使用することを避けることができます。この値を使用する必要があるコンポーネントが 1 つだけの場合は、この値が生成される場所でこのコンポーネントを生成し、props を使用してそれをコンポーネントの実際の表示場所にレイヤーごとに渡すことができます。
2. 基本的な使い方
1. カスタムコンテキスト
import React from 'react';
const ThemeContext = React.createContext('light');
export default ThemeContext;
上記のコードは、デフォルト値「light」を使用して ThemeContext を定義します。
2. 必要に応じてコンテキストのプロバイダーを使用する
import ThemeContext from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
function App() {
return (
<ThemeContext.Provider value='dark'>
<div className="App">
<header className="App-header">
<ThemedButton />
</header>
</div>
</ThemeContext.Provider>
);
}
export default App;
カスタム Context のプロバイダーはコンポーネントの最外層で使用され、受信値によってデフォルト値がオーバーライドされ、サブコンポーネントによって読み取られる ThemeContext の値はデフォルト値「light」ではなく「dark」になります。プロバイダーに値の定義がある場合、その値の値が使用されます (値が未定義、つまり値が渡されない場合でも)。プロバイダーが値を提供しない場合にのみ、その時点のデフォルト値が使用されます。の定義が使用されます。
3. contextType を定義し、Context から取得した値を使用します。
import React, {
Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
class ThemedButton extends Component {
static contextType = ThemeContext;
render() {
return <button>{
this.context}</button>;
}
}
export default ThemedButton;
ThemedButton は、contextType が ThemeContext であると宣言しているため、this.context の値は、最も近い ThemeContext によって提供される値 (「light」) になります。
レンダリング:
4. Context の名前
上記の方法で宣言された Context は、コンポーネント ツリーを表示するときに Context.Porvider のみを表示し、ThemeContext は表示しません。
これは、複数の Provider がある場合に表示するのに不便です。Context の表示名を定義するには、定義時に displayName で宣言できます。
import React from 'react';
const ThemeContext = React.createContext('light');
ThemeContext.displayName = 'ThemeContext';
export default ThemeContext;
このようにして、コンポーネント ツリーにコンテキストの名前が表示されます。
3. コンテキスト更新メカニズム
プロバイダーの値属性が変数の場合、値が変更されると、プロバイダーを使用するサブコンポーネントが更新メカニズムをトリガーし、それによって更新も実装されます。値が変化するかどうかは Object.is のメソッドによって判断されるため、次のような動的に生成されたオブジェクトを context の値として使用しないでください。
<ThemeContext.Provider value={
{
theme: 'light' }}><ThemeContext.Provider />
value が指す値はレンダリングされるたびに新しく生成されるオブジェクトであるため、メモリ ポインターは常に異なり、このプロバイダーを使用するコンポーネントの更新が毎回トリガーされます。したがって、変数を使用してこの値を伝えます。
1. コンテキストの更新によってトリガーされるApp.js のライフサイクル
import React, {
Component } from 'react';
import ThemeContext from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
theme: 'dark'
};
}
switchTheme = () => {
let {
theme } = this.state;
theme = theme === 'dark' ? 'light' : 'dark';
this.setState({
theme });
}
render() {
return (
<ThemeContext.Provider value={
this.state.theme}>
<div className="App">
<header className="App-header">
<ThemedButton onClick={
this.switchTheme} />
</header>
</div>
</ThemeContext.Provider>
);
}
}
export default App;
テーマボタン.js
import React, {
Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
class ThemedButton extends Component {
static contextType = ThemeContext;
componentDidUpdate() {
console.log('ThemedButton componentDidUpdate');
}
shouldComponentUpdate() {
console.log('ThemedButton shouldComponentUpdate');
return false;
}
render() {
console.log('ThemedButton render');
return <button onClick={
this.props.onClick}>{
this.context}</button>;
}
}
export default ThemedButton;
更新時の出力:
Context の更新は親コンポーネントとその独自の shouldComponentUpdate の影響を受けず、ライフサイクルは render->componentDidUpdate です。
2. props update と Context update は両方とも、
props update によってトリガーされるライフ サイクルは shouldComponentUpdate->render->componentDidUpdate です。Context 更新が発生すると、2 つの重複するライフ サイクル、つまり render->componentDidUpdate がトリガーされます。したがって、 shouldComponentUpdate ライフサイクルをトリガーする必要がある場合は、Context の更新と同時にトリガーされることを避ける必要があります。
4. Consumer を使用して複数の Context での値の取得をサポートする
1. 基本的な利用方法
複数のContextの値を利用する必要がある場合、contextTypeは1つだけでは不十分で、Contextが提供するConsumerを利用して、対応するContextの値を取得する必要があります。
2 つの Context タイプを宣言します。
import React from 'react';
const ThemeContext = React.createContext('light');
ThemeContext.displayName = 'ThemeContext';
export default ThemeContext;
import React from 'react';
const UserContext = React.createContext('guest');
UserContext.displayName = 'UserContext';
export default UserContext;
プロバイダー割り当てを使用します。
import React, {
Component } from 'react';
import ThemeContext from './context/ThemeContext.js';
import UserContext from './context/UserContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
}
render() {
return (
<ThemeContext.Provider value={
'dark'}>
<div className="App">
<UserContext.Provider value={
'user'}>
<header className="App-header">
<ThemedButton />
</header>
</UserContext.Provider>
</div>
</ThemeContext.Provider>
);
}
}
export default App;
ThemedButton のこれら 2 つのコンテキストによって提供される値を使用します。
import React, {
Component } from 'react';
import ThemeContext from "./context/ThemeContext.js";
import UserContext from "./context/UserContext.js";
class ThemedButton extends Component {
render() {
return (
<>
<ThemeContext.Consumer>
{
theme => <div>{
theme}</div>}
</ThemeContext.Consumer>
<UserContext.Consumer>
{
user => <div>{
user}</div>}
</UserContext.Consumer>
</>
);
}
}
export default ThemedButton;
Consumer パッケージのスコープ内では、対応する Provider が提供する値を使用できるため、コンポーネント内の複数の種類の Provider が提供する値を利用できます。
2. contextType と Consumer の比較
contextType はコンポーネント内の 1 つの Context のみを指すことができ、対応する Provider によって提供される値は this.context を通じてコンポーネント内のどこでも使用できます。欠点は、使用できるコンテキストが 1 つだけであることです。コンシューマは複数を使用して、さまざまなタイプのプロバイダの値を取得できます。ただし、タグの構文により、レンダー スコープと関連スコープでのみ使用できます。コンポーネントは、Context の値を取得するために可能な限り Consumer を使用し、後で contextType を使用する必要があるときに使用する contextType が存在しないことを避けるために、contextType の位置を残す必要があります。
5、カスタムコンテキスト
上記のContext定義では、Contextとそれに対応する表示名のみが定義されており、以降の利用にはContext.ProviderとContext.Consumerが使用されるため、このようなラベル言語は理解しにくいです。さらに、プロバイダーに値を割り当てるときに value 属性が使用されますが、特に複数の値が渡される場合、これは正確な意味を反映しません。したがって、Context をカスタマイズする場合は、次の部分が必要です。
import React from 'react';
import PropTypes from 'prop-types';
// 枚举值
const THEMES = {
DARK: 'dark',
LIGHT: 'light'
};
// 默认值
const defaultValue = THEMES.DARK;
// Context
const ThemeContext = React.createContext(defaultValue);
// Context展示名
ThemeContext.displayName = 'ThemeContext';
// Consumer
const ThemeConsumer = ThemeContext.Consumer;
// Provider
const ThemeProvider = ({
theme, children }) => (
<ThemeContext.Provider value={
theme}>
{
children}
</ThemeContext.Provider>
);
ThemeProvider.propTypes = {
theme: PropTypes.oneOf(['dark', 'light']),
children: PropTypes.oneOfType([
PropTypes.element,
PropTypes.arrayOf(PropTypes.element)
]),
}
export {
THEMES, ThemeContext, ThemeConsumer, ThemeProvider };
使用する場合:
import React, {
Component } from 'react';
import {
ThemeProvider, THEMES } from './context/ThemeContext.js';
import ThemedButton from './ThemedButton.js';
import './App.css';
class App extends Component {
render() {
// 不再需要ThemeContext.Provider和value,用更易理解的标签取代
return (
<ThemeProvider theme={
THEMES.LIGHT}>
<div className="App">
<header className="App-header">
<ThemedButton />
</header>
</div>
</ThemeProvider>
);
}
}
export default App;
import React, {
Component } from 'react';
import {
ThemeConsumer } from "./context/ThemeContext.js";
class ThemedButton extends Component {
render() {
// 用ThemeConsumer取代ThemeContext.Consumer
// 也可以用contextType = ThemeContext,ThemeContext也有导出
// 但是如之前所说,不到万不得已不使用只有一个位置的contextType
return (
<ThemeConsumer>
{
theme => <div>{
theme}</div>}
</ThemeConsumer>
);
}
}
export default ThemedButton;
六、useContextの使用
React はバージョン 16.8 で Hook を導入しました。これにより、クラスを作成せずに状態やその他の React 機能を使用できるようになります。useContext はネイティブ Hook の 1 つであり、Context も利用できる機能コンポーネントであり、複数の異なる種類の Context の利用をサポートしています。したがって、関数コンポーネントを使用する場合は、useContext を使用して、複数の異なるタイプの Context 値の使用をサポートできます。
Context の値が同じコードであることを指定します。
import React, {
Component } from 'react';
import {
ThemeProvider, THEMES } from './context/ThemeContext.js';
import UserContext from './context/UserContext';
import ThemeAndUser from './ThemeAndUser';
import './App.css';
class App extends Component {
render() {
return (
<ThemeProvider theme={
THEMES.LIGHT}>
<UserContext.Provider value={
'user'}>
<div className="App">
<header className="App-header">
<ThemeAndUser />
</header>
</div>
</UserContext.Provider>
</ThemeProvider>
);
}
}
export default App;
useContext を使用して Context の値を取得します。
import {
useContext } from 'react';
import {
ThemeContext } from './context/ThemeContext';
import UserContext from './context/UserContext';
const ThemeAndUser = () => {
const theme = useContext(ThemeContext);
const user = useContext(UserContext);
return (
<>
<div>{
theme}</div>
<div>{
user}</div>
</>
);
};
export default ThemeAndUser;
7. まとめ
コンテキストは、制御可能な更新メカニズムを備えたグローバルにアクセス可能なコンテンツを提供するために使用されます。ただし、必要な場合以外は使用しないようにしてください。
コンテキストは値だけでなく関数も渡すことができます。値を更新する関数と一緒にコンテキストで値を渡すことができます。
contextType は 1 つの Context のみを指し、1 つの Context 値を使用できます。Context.Consumer を使用すると、複数の Context 値を使用できますが、これは render とその関連関数でのみ使用できます。
useContext は複数の Context 値を使用することもできますが、関数コンポーネント内でのみ使用できます。
8. 参考文献
1. Context の使用に関する React の公式中国語ドキュメント: https://zh-hans.reactjs.org/docs/context.html
2. useContext の使用に関する React の公式中国語ドキュメント: https://zh-hans.reactjs.org/docs/hooks-reference.html#usecontext
———————————————
著作権表示: この記事は CSDN ブロガー「syukehan」のオリジナル記事です。CC 4.0 BY-SA 著作権契約に従って、オリジナルのソースリンクとこれを添付してください。転載用ステートメント。
元のリンク: https://blog.csdn.net/syukehan/article/details/114039009