2

私はこれの概念実証を持っていますが、これはうまくいくように見えますが、これが本当に良いアイデアなのか、Redux や代替戦略などを使用したより良い解決策があるのか​​ どうか、私の一部は疑問に思っています.

問題

基本的に、アプリケーション全体の基本的な React コンポーネントがあり、ヘッダー、メニュー、フッターなど、期待される典型的なコンポーネントがたくさんあります。

ツリーのさらに下 (さらに下) に、ヘッダー コンポーネント内に新しいメニュー項目をマウントできればすばらしいコンポーネントがあります。もちろん、ヘッダー コンポーネントはアプリケーションの一番上にあるため、アクセスは拒否されます。

それはほんの一例ですが、私がいろいろな角度から突きつけてきた問題事例です。

私のクレイジーな解決策

子コンポーネントがヘッダー内に表示したい追加要素を宣言できるようにする関数を公開するために、React のコンテキストを使用することを検討しました。

コンセプトをいじってみた後、最終的に、本質的に React Element メッセージング システムであるかなり一般的なソリューションにリファクタリングしました。このソリューションには 3 つの部分があります。

1. プロバイダー

Redux の Connect コンポーネントと同様の単一インスタンス コンポーネント。彼女は本質的に、メッセージを受信して​​渡すエンジンです。彼女の基本的な構造 (コンテキスト重視) は次のとおりです。

class ElementInjectorProvider extends Component {
  childContextTypes: {

    // :: (namespace, [element]) -> void
    produceElements: PropTypes.func.isRequired,

    // :: (namespace, [element]) -> void
    removeElements: PropTypes.func.isRequired,

    // :: (listener, namespace, ([element]) -> void) -> void
    consumeElements: PropTypes.func.isRequired,

    // :: (listener) -> void
    stopConsumingElements: PropTypes.func.isRequired,

  }

  /* ... Implementation ... */
}

2.プロデューサー

より高次のコンポーネント。各インスタンスは、コンテキスト アイテムを介して要素を「生成」produceElementsし、特定の名前空間に要素を提供してから、(コンポーネントのアンマウントの場合) を介して要素を削除できremoveElementsます。

function ElementInjectorProducer(config) {
  const { namespace } = config;

  return function WrapComponent(WrappedComponent) {
    class ElementInjectorConsumerComponent {
      contextTypes = {
        produceElements: PropTypes.func.isRequired,
        removeElements: PropTypes.func.isRequired
      }

      /* ... Implementation ... */
    }

    return ElementInjectorProducerComponent;
  };
}

3. 消費者

より高次のコンポーネント。各インスタンスは、特定の名前空間に関連付けられた要素を「監視」するように構成されています。consumeElementsコールバック関数の登録を介してリッスンを「開始」しstopConsumingElements、消費を登録解除するために使用します。

function ElementInjectorConsumer(config) {
  const { namespace } = config;

  return function WrapComponent(WrappedComponent) {
    class ElementInjectorConsumerComponent {
      contextTypes = {
        consumeElements: PropTypes.func.isRequired,
        stopConsumingElements: PropTypes.func.isRequired
      }

      /* ... Implementation ... */
    }

    return ElementInjectorConsumerComponent;
  };
}

それは私がやろうとしていることの大まかな概要です。基本的に、これはメッセージング システムです。そして、おそらくさらに抽象化することができます。

私はすでに redux を使用していますが、Redux が何に適していると思いますか? したがって、これは私にとってはうまくいっていますが、おそらくそれは良い設計ではなく、うっかりして Redux のつま先に立ったり、一般的なアンチパターンを作成したりしたと感じずにはいられません。

このために Redux をすぐに使用しなかった唯一の理由は、単純な状態ではなく要素を作成しているためだと思います。要素記述子オブジェクトを作成し、それを Redux 経由で渡すこともできますが、それ自体が複雑です。


知恵の言葉はありますか?


更新番号 1

上記に関する追加の説明。

これにより、完全なコンポーネント ツリーに要素を上下、さらには左から右に挿入できます。ほとんどの React Context の例では、祖父母から孫コンポーネントへのデータの注入について説明していることを知っています。

また、上記の実装では、コンテキストの使用に関する知識を開発者から抽象化する必要があります。実際、私はおそらくこれらの HOFS を使用して、ユース ケースに固有ではるかに明示的な追加のラッパーを作成するでしょう。

すなわち

消費者の実装:

<InjectableHeader />

プロデューサーの実装:

InjectIntoHeader(<FooButton />)(FooPage)

それは私が考えるかなり明確で、従うのは簡単です。ボタンを最も気にかけている場所に作成できるので、仲間とのより強い関係を築くことができます。

また、redux フローはおそらく正しい考えだと思います。自分自身でそれを難し​​くしているように感じます-このテクニックには何らかのメリットがあるのではないかと思わずにはいられません.

これが特に悪い考えである理由はありますか?


更新番号 2

わかりました、これは悪い考えだと確信しています。私は基本的に、アプリケーションの予測可能性を壊し、一方向データ モデルが提供するすべての利点を無効にしています。

Redux を使用することがこの場合の最適なソリューションであるとはまだ確信が持てませんが、コンテキスト マジックを使用せずに、上記の概念のいくつかを使用する、より明確な単方向ソリューションを思いつきました。

うまくいくと思われる場合は、解決策を回答として投稿します。それができない場合は、Redux に行って、すぐに皆さんの話を聞かなかったことを自責します。


その他の例

さまざまな手法を使用して同じ(っぽい)問題を解決しようとしている他のいくつかのプロジェクト/アイデアを次に示します。

https://joecritchley.svbtle.com/portals-in-reactjs

https://github.com/davidtheclark/react-displace

https://github.com/carlsverre/react-outlet

4

2 に答える 2

3

redux/flux/reflux/anthingelseux をいつ使用し、いつ context を使用するかについての私の考え:

  • -ux ストアは、コンポーネント間で共有される情報を横方向に保存するのに役立ちます。これは通常、他に明らかな接続がなく、ツリー内で互いに遠く離れているコンポーネント間の通信です。
  • コンテキストは、子供がどこにいるのか、または何人の子供が必要になるのかわからない場合に、子供に情報を提供するのに役立ちます。たとえば、マップのコンテキストを使用して、現在のビューポートの子に情報を提供します。すべての子供たちがそれを使用するかどうかはわかりませんが、彼らはすべて潜在的に興味を持っており、変更することは想定されていません.

あなたの場合、 -ux の方法は彼らのやり方だと思います.ラッパーコンポーネントが何の関係もないロジックを処理する必要がある理由はなく、コードは不明瞭になります。開発者が後でコードにアクセスし、コンテキストを通じてこれを受け取ることを想像してください。どの親もそれを送信した可能性があるため、どこに送信されたかを確認する必要があります。ラッパー コンポーネントにも同じことが起こります。同様の操作が増加し始めると、そこでは何の関係もない多くのメソッドとハンドラーを処理することになります。

アクションとリデューサーを備えたストアを持つことで、ケースの懸念を分離することができ、物事を行うための最も読みやすい方法になります。

于 2016-04-01T15:07:08.117 に答える
1

わかりました。おそらく、Redux とその一方向のデータ フローを使用することが最善の解決策であるというのが賢明でしょう。そのため、@Mijamo による回答を回答として設定しました。

私は自分の投稿で話していた注射剤ソリューションを作成することになりました. これまでのところ、非常に役に立ちました。これは実際に非常に便利で、他の手法を使用すると複雑すぎる素晴らしいものを作成することができました。

何よりも良いことは、私の注入可能なターゲットが、注入される可能性のあるすべてのコンポーネントについて明示的に知る必要がないことです。

ライブラリを作成したことにとても満足しています。

https://github.com/ctrlplusb/react-injectables

ご覧のとおり、コンポーネント バインディング (ターゲット/ソース) をできるだけ明示的にしようとしています。コード内のそれぞれのすべてのターゲットを実際にインポートしてバインドする必要があります。これは、ターゲット/ソース バインディングのコンパイル時のチェックを取得できるため (まあまあ) 便利です。マジック ストリング ベースのバインディングよりもはるかに便利です。

とにかく、それはおそらくまだクレイジーなアイデアかもしれませんが、おそらく私はクレイジーなので、とても気に入っています. :)

于 2016-04-06T16:21:13.707 に答える