ホームページ  >  記事  >  ウェブフロントエンド  >  反応におけるコンテキストとは何ですか

反応におけるコンテキストとは何ですか

WBOY
WBOYオリジナル
2022-06-28 10:55:372468ブラウズ

react では、コンテキストは、コンポーネントの各レイヤーに手動で props を追加することなく、コンポーネント ツリー間でデータを転送するためのメソッドです。コンテキストは、コンポーネント間で指定された値を共有する方法を提供し、明示的に渡す必要はありませんコンポーネントツリーを介してpropします。

反応におけるコンテキストとは何ですか

このチュートリアルの動作環境: Windows 10 システム、react17.0.1 バージョン、Dell G3 コンピューター。

react のコンテキストとは

Context は、コンポーネントの各レイヤーにプロップを手動で追加することなく、コンポーネント ツリー間でデータを転送する方法を提供します。典型的な React アプリケーションでは、データは props を介して上から下 (親から子) に渡されますが、このアプローチは特定の種類のプロパティ (例: ロケール設定、UI テーマ) にとっては非常に面倒です。これらのプロパティは、多くのコンポーネントで必要となります。アプリケーション。 Context は、コンポーネント ツリーの各レベルで明示的に props を渡すことなく、コンポーネント間でそのような値を共有する方法を提供します。

コンテキストいつ使用するか?

コンテキストは、現在認証されているユーザー、テーマ、優先言語など、コンポーネント ツリーに対して「グローバル」なデータを共有するように設計されています。たとえば、次のコードでは、「テーマ」属性を通じてボタン コンポーネントのスタイルを手動で調整します

class App extends React.Component {
  render() {
    return <Toolbar theme="dark" />;
  }
}

function Toolbar(props) {
  // Toolbar 组件接受一个额外的“theme”属性,然后传递给 ThemedButton 组件。
  // 如果应用中每一个单独的按钮都需要知道 theme 的值,这会是件很麻烦的事,
  // 因为必须将这个值层层传递所有组件。
  return (
    <p>
      <ThemedButton theme={props.theme} />
    </p>
  );
}

class ThemedButton extends React.Component {
  render() {
    return <Button theme={this.props.theme} />;
  }
}

// 通过props传递:App -> Toolbar -> ThemedButton
// 如果嵌套很深,那么需要逐层传递props,即使中间不需要该props,显得很繁琐

コンテキストを使用すると、中間要素を介して props を渡すことを回避できます

// Context 可以让我们无须明确地传遍每一个组件,就能将值深入传递进组件树。
// 为当前的 theme 创建一个 context("light"为默认值)。
const ThemeContext = React.createContext('light');
class App extends React.Component {
  render() {
    // 使用一个 Provider 来将当前的 theme 传递给以下的组件树。
    // 无论多深,任何组件都能读取这个值。
    // 在这个例子中,我们将 “dark” 作为当前的值传递下去。
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

// 中间的组件再也不必指明往下传递 theme 了。
function Toolbar() {
  return (
    <p>
      <ThemedButton />
    </p>
  );
}

class ThemedButton extends React.Component {
  // 指定 contextType 读取当前的 theme context。
  // React 会往上找到最近的 theme Provider,然后使用它的值。
  // 在这个例子中,当前的 theme 值为 “dark”。
  static contextType = ThemeContext;
  render() {
    return <Button theme={this.context} />;
  }
}
// 也可以使用 ThemedButto.contextType = ThemeContext;

API の概要

React.createContext

const MyContext = React.createContext(defaultValue);

Context オブジェクトを作成します。 React がこの Context オブジェクトにサブスクライブされたコンポーネントをレンダリングするとき、コンポーネントはコンポーネント ツリー内でそれ自体に最も近い一致する Provider から現在のコンテキスト値を読み取ります。

コンポーネントが配置されているツリーに一致するプロバイダーがない場合にのみ、その defaultValue パラメーターが有効になります。これは、コンポーネントをプロバイダーでラップせずにテストするのに役立ちます。注: unknown がプロバイダーの値に渡されると、使用側コンポーネントの defaultValue は有効になりません。

Context.Provider

<MyContext.Provider value={/* 某个值 */}>

各 Context オブジェクトは Provider React コンポーネントを返します。これにより、使用するコンポーネントがコンテキストの変更をサブスクライブできるようになります。

プロバイダーは value 属性を受け取り、それを使用側コンポーネントに渡します。プロバイダーは、複数のコンシューマー コンポーネントと対応する関係を持つことができます。複数のプロバイダーをネストして使用することもでき、内側のレイヤーが外側のレイヤーのデータを上書きします。

プロバイダーの value 値が変更されると、その内部にあるすべての使用コンポーネントが再レンダリングされます。プロバイダーもその内部コンシューマー コンポーネントも shouldComponentUpdate 関数の対象ではないため、コンシューマー コンポーネントは、その祖先コンポーネントが更新を終了した場合でも更新できます。

Class.contextType

クラスにマウントされた contextType 属性は、React.createContext() オブジェクトによって作成された Context に再割り当てされます。これにより、this.context を使用して最新のコンテキストの値を使用できるようになります。レンダリング関数

import MyContext from './MyContext';

class MyClass extends React.Component {
  componentDidMount() {
    let value = this.context;
    /* 在组件挂载完成后,使用 MyContext 组件的值来执行一些有副作用的操作 */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* 基于 MyContext 组件的值进行渲染 */
  }
  // 或者如上边例子一样使用 static contextType = MyContext;
}
MyClass.contextType = MyContext;

Context.Consumer

import MyContext from './MyContext';

function ToolList() {
  return (
    <MyContext.Consumer
      {value => /* 基于 context 值进行渲染*/}
    </MyContext.Consumer>
  )
}

を含め、あらゆるライフサイクルでアクセスできます。ここでは、Reactコンポーネントはコンテキストの変更をサブスクライブすることもできます。これにより、機能コンポーネントのコンテキストをサブスクライブできるようになります。

これには子としての機能が必要です。この関数は現在のコンテキスト値を受け取り、React ノードを返します。関数に渡される value 値は、コンポーネント ツリーの上でこのコンテキストに最も近いプロバイダーによって提供される value 値と同等です。対応するプロバイダーがない場合、value パラメーターは、createContext() に渡される defaultValue と同等です。

Context.displayName

コンテキスト オブジェクトは、文字列型の displayName という名前のプロパティを受け入れます。 React DevTools はこの文字列を使用して、表示するコンテキストを決定します。

次のコンポーネントは、DevTools で MyDisplayName として表示されます

const MyContext = React.createContext(/* some value */);
MyContext.displayName = 'MyDisplayName';

<MyContext.Provider>   // "MyDisplayName.Provider" 在 DevTools 中
<MyContext.Consumer>   // "MyDisplayName.Consumer" 在 DevTools 中

ダイナミック コンテキスト

上記のテーマの例では、より複雑な使用法のために動的な値を使用します

theme-context.js

export const themes = {
  light: {
    foreground: '#000000',
    background: '#eeeeee',
  },
  dark: {
    foreground: '#ffffff',
    background: '#222222',
  },
};

export const ThemeContext = React.createContext(themes.dark);   // 该处为默认值

themed- button.js

import { ThemeContext } from './theme-context';

class ThemedButton extends React.Component {
  render() {
    let props = this.props;
    // 获取到ThemeContext中的默认值
    let theme = this.context;
    return (
      <button
        {...props}
        style={{backgroundColor: theme.background}}
      />
    );
  }
  // static contextType = ThemeContext;
}
ThemedButton.contextType = ThemeContext;

export default ThemedButton;

app.js

import { ThemeContext, themes } from './theme-context';
import ThemedButton from './themed-button';

// 一个使用 ThemedButton 的中间组件
function Toolbar(props) {
  return (
    <ThemedButton onClick={props.changeTheme}>
      Change Theme
    </ThemedButton>
  );
}

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      theme: themes.light,
    };

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };
  }

  render() {
    // 在 ThemeProvider 内部的 ThemedButton 按钮组件使用 state 中的 theme 值,
    // 而外部的组件使用默认的 theme 值
    return (
      <Page>
        <ThemeContext.Provider value={this.state.theme}>
          <Toolbar changeTheme={this.toggleTheme} />
        </ThemeContext.Provider>
        <Section>
          <ThemedButton />
        </Section>
      </Page>
    );
  }
}

ReactDOM.render(<App />, document.root);

// 使用ThemeContext.Provider包裹的组件,可以消费到ThemeContext中的value
// 即Toolbar、ThemedButton中都可以使用this.context来获取到value
// 注意观察,更新state的方法是通过props向下传递,由子孙组件触发更新,下面会讲到通过context的方式传递更新函数

在嵌套组件中更新 Context

在上面的例子中,我们通过 props 的方式向下传递一个更新函数,从而改变 App 中 themes 的值。我们知道,从一个在组件树中嵌套很深的组件中更新 context 是很有必要的。在这种场景下,你可以通过 context 传递一个函数,使得 consumers 组件更新 context

theme-context.js

// 确保传递给 createContext 的默认值数据结构是调用的组件(consumers)所能匹配的!
export const ThemeContext = React.createContext({
  theme: themes.dark,
  toggleTheme: () => {},   // 定义更新主题的方法,向下传递
});

theme-toggler-button.js

import { ThemeContext } from './theme-context';

function ThemeTogglerButton() {
  // Theme Toggler 按钮不仅仅只获取 theme 值,它也从 context 中获取到一个 toggleTheme 函数(下面app.js部分)
  return (
    <ThemeContext.Consumer>
      {({theme, toggleTheme}) => (
        <button onClick={toggleTheme} style={{backgroundColor: theme.background}}>
          Toggle Theme
        </button>
      )}
    </ThemeContext.Consumer>
  );
}

export default ThemeTogglerButton;

app.js

import { ThemeContext, themes } from './theme-context';
import ThemeTogglerButton from './theme-toggler-button';

class App extends React.Component {
  constructor(props) {
    super(props);

    this.toggleTheme = () => {
      this.setState(state => ({
        theme: state.theme === themes.dark ? themes.light : themes.dark,
      }));
    };

    // State 也包含了更新函数,因此它会被传递进 context provider。
    this.state = {
      theme: themes.light,
      toggleTheme: this.toggleTheme,   // 定义更新函数,通过context方式向下传递
    };
  }

  render() {
    // 整个 state 都被传递进 provider
    return (
      <ThemeContext.Provider value={this.state}>
        <Content />
      </ThemeContext.Provider>
    );
  }
}

function Content() {
  return (
    <p>
      <ThemeTogglerButton />
    </p>
  );
}

ReactDOM.render(<App />, document.root);

消费多个 Context

为了确保 context 快速进行重渲染,React 需要使每一个 consumers 组件的 context 在组件树中成为一个单独的节点

// Theme context,默认的 theme 是 "light" 值
const ThemeContext = React.createContext('light');

// 用户登录 context
const UserContext = React.createContext({
  name: 'Guest',
});

class App extends React.Component {
  render() {
    const { signedInUser, theme } = this.props;

    // 提供初始 context 值的 App 组件
    return (
      <ThemeContext.Provider value={theme}>
        <UserContext.Provider value={signedInUser}>
          <Layout />
        </UserContext.Provider>
      </ThemeContext.Provider>
    );
  }
}

function Layout() {
  return (
    <p>
      <Sidebar />
      <Content />
    </p>
  );
}

// 一个组件可能会消费多个 context
function Content() {
  return (
    <ThemeContext.Consumer>
      {theme => (
        <UserContext.Consumer>
          {user => (
            <ProfilePage user={user} theme={theme} />
          )}
        </UserContext.Consumer>
      )}
    </ThemeContext.Consumer>
  );
}

如果两个或者更多的 context 值经常被一起使用,那你可能要考虑一下另外创建你自己的渲染组件,以提供这些值。

注意事项

因为 context 会使用参考标识(reference identity)来决定何时进行渲染,这里可能会有一些陷阱,当 provider 的父组件进行重渲染时,可能会在 consumers 组件中触发意外的渲染。举个例子,当每一次 Provider 重渲染时,以下的代码会重渲染所有下面的 consumers 组件,因为 value 属性总是被赋值为新的对象

class App extends React.Component {
  render() {
    return (
      <MyContext.Provider value={{something: &#39;something&#39;}}>
        <Toolbar />
      </MyContext.Provider>
    );
  }
}

为了防止这种情况,将 value 状态提升到父节点的 state 里

class App extends React.Component {
  constructor(props) {
    super(props);
    // 多次渲染,state 会被保留,当value不变时,下面的 consumers 组件不会重新渲染 
    this.state = {
      value: {something: 'something'},
    };
  }

  render() {
    return (
      <Provider value={this.state.value}>
        <Toolbar />
      </Provider>
    );
  }
}

【相关推荐:javascript视频教程web前端

以上が反応におけるコンテキストとは何ですかの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。