私はこれの概念実証を持っていますが、これはうまくいくように見えますが、これが本当に良いアイデアなのか、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