ホームページ >ウェブフロントエンド >jsチュートリアル >React のスコープ付きコンテキストを例を使って説明する

React のスコープ付きコンテキストを例を使って説明する

Barbara Streisand
Barbara Streisandオリジナル
2024-12-26 09:11:10693ブラウズ

Explaining Scoped Context in React with example

React コンテキストはグローバル変数です

JavaScript では、変数のスコープは関数定義内にあります。

React のスコープ付きコンテキストを例で説明する

React Context は、グローバル状態を管理するメカニズムとして説明されることが多く、React コンポーネント ツリー全体でアクセスできる共有変数として機能します。この説明は正確ですが、Context の機能を単純化しすぎています。この記事では、Context のスコープを効果的に設定し、必要な場合にのみ Context が使用され、不必要な再レンダリングを回避する方法について詳しく説明します。

React コンテキストとは何ですか?

React Context は、すべてのレベルで props を手動で渡すことなく、コンポーネント ツリーを通じてデータを渡す方法を提供します。これは React.createContext を使用して作成され、プロバイダーとコンシューマーのペアで構成されます。 Provider コンポーネントは値を提供し、Consumer または useContext フックでラップされたコンポーネントはその値にアクセスできます。

これが基本的な例です:

import React, { createContext, useContext } from "react";

const ThemeContext = createContext("light");

function App() {
  return (
    <ThemeContext.Provider value="dark">
      <Toolbar />
    </ThemeContext.Provider>
  );
}

function Toolbar() {
  return <ThemedButton />;
}

function ThemedButton() {
  const theme = useContext(ThemeContext);
  return <button>{`Theme: ${theme}`}</button>;
}

export default App;

この例では、ThemedButton は、ツールバーを介して明示的に props を渡すことなく、ThemeContext.Provider によって提供されるテーマ値にアク​​セスできます。

なぜスコープ付きコンテキストなのか?

Context は強力ですが、無差別に使用するとパフォーマンスの問題が発生する可能性があります。 Context.Provider によって提供される値が変更されると、そのコンテキストを使用するすべてのコンポーネントが再レンダリングされます。複雑なアプリケーションでは、これにより、無関係なコンポーネントが不必要に再レンダリングされる可能性があります。

スコープ付きコンテキストとは、コンテキストの使用を実際に必要とするコンポーネント ツリーの部分のみに制限する実践を指します。このアプローチは、パフォーマンスを維持し、コンポーネント構造をクリーンで理解しやすい状態に保つのに役立ちます。

複合コンポーネントの課題

Radix Primitives などのライブラリによって提供されるコンポーネントなど、複合コンポーネントが関与するシナリオを考えてみましょう。これらのコンポーネントは多くの場合、内部でコンテキストを使用して状態と対話を管理します。ただし、同様のコンポーネントを一緒に構成すると問題が発生し、コンテキストの衝突が発生する可能性があります。

基数プリミティブの例

Radix Primitives は、アクセス可能なコンポーネントを構築するための高度に構成可能な API を提供します。以下に例を示します:

<AlertDialog.Root>
  <Dialog.Root>
    <Dialog.Trigger />
    <Dialog.Content>
      <AlertDialog.Trigger /> {/* note the alert trigger in dialog content */}
    </Dialog.Content>
  </Dialog.Root>

  <AlertDialog.Content />
</AlertDialog.Root>

AlertDialog は、AlertDialog の要件を満たす追加機能を備えた Dialog を組み合わせたものであるため、ここで問題が発生します。これは、AlertDialog.Root も Dialog.Root であることを意味し、DialogContext と AlertDialogContext の両方を提供します。

この設定では、AlertDialog.Trigger (Dialog.Trigger でもあります) が useContext(DialogContext) を介して間違ったコンテキストを取得し、AlertDialog.Root ではなく Dialog.Root からのコンテキストを取得する可能性があります。その結果、AlertDialog.Trigger をクリックすると、意図したとおりに動作せず、Dialog.Content が切り替わる可能性がありました。

スコープ指定されたコンテキスト ソリューション

このような問題を防ぐために、Radix Primitives はスコープ付きコンテキストを使用します。スコープ付きコンテキストにより、AlertDialog.Trigger が AlertDialog 部分とのみ対話し、同様に構成されたコンポーネントから誤ってコンテキストを取得しないことが保証されます。これは、内部で新しいコンテキストを作成し、それを __scopeDialog などのカスタム プロップを通じて Dialog コンポーネントに渡すことによって実現されます。 Dialog コンポーネントは、useContext 呼び出しでこのスコープ指定されたコンテキストを使用し、分離を確保します。

radix ui github リポジトリからのソース コード:

https://github.com/radix-ui/primitives/blob/dae8ef4920b45f736e2574abf23676efab103645/packages/react/dialog/src/Dialog.tsx#L69

Radix UI でスコープ付きコンテキストがどのように機能するかを抽象化したものを次に示します。

  1. スコープの作成: createScope ユーティリティは、コンポーネントまたは複合コンポーネントごとに一意の名前空間を生成します。これにより、コンテキストの各セットが分離され、他のコンテキストと競合しないことが保証されます。

    import React, { createContext, useContext } from "react";
    
    const ThemeContext = createContext("light");
    
    function App() {
      return (
        <ThemeContext.Provider value="dark">
          <Toolbar />
        </ThemeContext.Provider>
      );
    }
    
    function Toolbar() {
      return <ThemedButton />;
    }
    
    function ThemedButton() {
      const theme = useContext(ThemeContext);
      return <button>{`Theme: ${theme}`}</button>;
    }
    
    export default App;
    
  2. スコープ付きプロバイダー: コンテキストを作成すると、コンテキストはスコープに関連付けられます。これにより、プロバイダーとコンシューマーが同じ名前空間にバインドされます。

    <AlertDialog.Root>
      <Dialog.Root>
        <Dialog.Trigger />
        <Dialog.Content>
          <AlertDialog.Trigger /> {/* note the alert trigger in dialog content */}
        </Dialog.Content>
      </Dialog.Root>
    
      <AlertDialog.Content />
    </AlertDialog.Root>
    
  3. コンシューマの分離: useDialogScope などのスコープ付きフックにより、コンシューマが意図したスコープのコンテキストのみにアクセスできるようになります。

    import { createScope } from '@radix-ui/react-context';
    
    const [createDialogContext, useDialogScope] = createScope('Dialog');
    

スコープ付きコンテキストの利点

  • コンテキスト衝突防止: コンテキストをスコープすることにより、AlertDialog.Trigger などのコンポーネントは、他のコンテキスト内にネストされている場合でも、関連するコンテキスト (AlertDialogContext) を常に見つけることができます。

  • 柔軟な構成: スコープ付きコンテキストにより、コンポーネントの柔軟かつ安全な構成が可能になり、対話が予測可能な状態に保たれます。

  • 再利用性: 開発者は、汎用コンポーネント (Dialog.Trigger など) を変更せずにさまざまなスコープで再利用できます。

例への適用方法

あなたの例では:

  • AlertDialog.Root は、その状態と対話をカプセル化するスコープ付き AlertDialogContext を作成します。

  • ネストされた Dialog.Root と AlertDialog.Trigger は、それぞれがそれぞれのスコープ付きコンテキストを参照するため、競合することなく共存します。

  • このデザイン パターンは Radix UI の重要な機能であり、複雑なコンポーネント階層が意図しない動作を起こすことなくシームレスに動作することを保証します。

参考文献:

  1. https://dev.to/romaintrotard/use-context-selector-demystified-4f8e

  2. https://github.com/radix-ui/primitives

  3. https://react.dev/reference/react/createContext

以上がReact のスコープ付きコンテキストを例を使って説明するの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

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