TypeScriptデコレータを使用してReactコンポーネントと通信する方法

Reactでアプリケーションを開発する場合、独立したコンポーネントを作成するのはかなり不便です。 それらの間でデータを交換する標準的な方法は「状態を持ち上げる 」です。 このアプローチは、中間コンポーネントを不必要な特性で徐々に汚染し、再利用するには不便です。



画像






この問題(およびその他の問題)を解決する最も一般的な方法は、ReduxやMobxなどのライブラリです。これにより、データを別の場所に保存し、コンポーネントに直接転送できます。 この記事では、この問題を解決するためのアプローチを示したいと思います。



Docsvision EDSの個別のページは、目的の位置に配置されたさまざまなReactコンポーネントから特別なWYSIWYGエディターで組み立てられます。



画像






パートナーは、APIを介してコンポーネントと対話するJavaScriptスクリプトを記述したり(プロパティの読み取り/書き込み、メソッドの呼び出し)、通常のJSX構文を介して同じコンポーネントをレンダリングしたりできます(たとえば、ある種のモーダルウィンドウまたは独自のコンポーネント内)。



したがって、コンポーネントは相互作用する3つの方法を提供する必要があります。



  1. WYSIWYGエディターで構成されたサーバーから設定を取得します。
  2. JavaScriptスクリプトを使用した相互作用。
  3. JSXを介した相互作用。


これらすべての相互作用方法をサポートするために、プロパティの標準メカニズムをわずかに改善するパラメーターシステムを開発しました。 次のようになります。



//         . let textBox = layout.controls.textBox; //       . textBox.params.value = " "; //       . textBox.params.dataChanged = (sender, data)=> sender.params.value = data.newValue.toUpperCase();
      
      





そして、これは、クラス自体がTextBoxコンポーネントのパラメータを操作する方法です。



 class TextBoxParams { /** . */ @rw value?: string = ''; /**    . */ @r canEdit?: boolean; /** ,    . */ @apiEvent dataChanged?: BasicApiEvent<string>; }
      
      





ご覧のとおり、プロパティの標準的なメカニズムのように、通常のプロパティの列挙に加えて、デコレーター@ r、@ rw、@ apiEventもあります。 彼らの助けを借りて、私たちはプロパティに対してより柔軟な動作を作成します。



また、同じクラスがReactプロパティのインターフェイスとしても使用されるため、外部スクリプトとJSXの両方でコンポーネントと同等に対話できます。









プロパティに最も一般的に使用されるデコレータは次のとおりです。



デコレータ名 説明
  @r 
プロパティは読み取り専用であり、変更できません。
  @rw 
このプロパティは、読み取りと書き込みの両方に使用できます。
  @apiEvent 
プロパティの値を、同じ名前のコンポーネントのイベントのハンドラーと見なす必要があることを示します。 また、このようなプロパティを使用する場合、イベント固有のロジックを実装します(たとえば、新しいプロパティ値を設定するときに、以前のハンドラーから自動的にサブスクライブを解除します)。
  @handler(paramName) 
上記のリストとは異なり、このデコレーターはプロパティではなく、コンポーネント内のゲッターまたはセッターにかかっています。 これにより、プロパティ値の書き込みまたは読み取り時に独自のロジックを追加できます。 たとえば、値の先頭と末尾からスペースを削除する場合:



 class TextBoxParams { /** . */ @rw value?: string = ''; } class TextBox extends BaseControl<TextBoxProps, TextBoxState> { ... @handler('value') private get value(): string { return this.state.value.trim(); } ... }
      
      







同時に、デコレータ自体には通常、ビジネスロジックは含まれませんが、使用されたデコレータの種類に関する情報のみが保存されます。 これは、 reflect-metadataライブラリを使用し行われ、ロジックを別の場所に保存し、複数の添付されたメタデータを柔軟に結合できるという点で便利です。 @rデコレータを使用した簡単な例でこのライブラリを使用することを検討してください。



 //        @r. const READONLY_DECORATOR_METADATA_KEY = "CONTOL_PUBLIC_API_READONLY"; //   @r,       . export function r(target: Object, propertyKey: string | symbol) { Reflect.defineMetadata(READONLY_DECORATOR_METADATA_KEY, true, target, propertyKey); } //        @r  . export function isReadonly(target: Object, propertyKey: string): boolean { return Reflect.getMetadata(READONLY_DECORATOR_METADATA_KEY, target, propertyKey); }
      
      





このデコレータをオブジェクトのプロパティに適用すると、「CONTOL_PUBLIC_API_READONLY」という名前のメタデータと値trueがこのプロパティに自動的にバインドされます。



このようなメタデータを使用して、パラメーターに必要な動作を動的に設定できます(アクセス修飾子、上記のテーブルのイベントの操作など)。 最も簡単な実装の例を、ネタバレの下に示します。



サンプル実装コード
 class TextAreaParams { @r value: string = ''; } /** .  1 . */ interface ITextAreaState extends TextAreaParams { } class TextArea extends React.Component<TextAreaParams, ITextAreaState> { /** .  2 . */ params: TextAreaParams = {} as TextAreaParams; constructor(props: ITextAreaProps) { super(props); /** .  3 . */ this.state = new TextAreaParams() as ITextAreaState; /** .  4 . */ for (let propName in this.state) { let descriptor = { get: () => this.getParamValue(propName), set: (value: any) => this. (propName, value), configurable: true, enumerable: true } as PropertyDescriptor; Object.defineProperty(this.params, propName, descriptor); } /** .  5 . */ for (let propName in this.props) { this.setParamValue(propName, this.props[propName], true); } } /** .  6 . */ componentWillReceiveProps(nextProps: ITextAreaProps) { for (let propName in this.props) { if (this.props[propName] != nextProps[propName]) { this.setParamValue(propName, this.props[propName]); } } } /** .  7 . */ getParamValue(paramName: string) { return this.state[paramName]; } /** .  8 . */ setParamValue(paramName: string, value: any, initial: boolean) { const readOnly = isReadonly(this.state, paramName); if (!readOnly || initial) { this.state[paramName] = val; this.forceUpdate(); } else { if (this.props[paramName] != value) { console.warn(" " + paramName + "    ."); } } } }
      
      





  1. 状態コンポーネントのインターフェイスは、Paramsクラスから継承されるため、内部のデータの均一性が確保されます。 さらに、同じクラスがプロパティのインターフェイスとしても使用されます。
  2. paramsの今後の作業のために空のオブジェクトを作成します。 その中のプロパティは後で入力されます。
  3. paramsのクラスのインスタンスである状態コンポーネントを作成します。
  4. paramsプロパティを入力します。 ご覧のとおり、paramsオブジェクト自体はデータを保存しませんが、getParamValueメソッドとsetParamValueメソッドをゲッターおよびセッターとして使用します。
  5. 初期プロパティ値をparamsと同期します。
  6. 新しい小道具が受信されると、それらもparamsと同期します。 これと前のパラグラフに基づいて、Reactプロパティがパラメーターを介してコンポーネントの状態に値を渡すことは明らかであり、これによりデコレーターも使用できるようになります。
  7. パラメータの値は、stateから同じ名前のプロパティから単純に取得されます。 私たちにとってそれは「唯一の真実の源」です。
  8. 新しいパラメーター値を設定するとき、上記で作成したisReadonlyヘルパーを使用して、@ rデコレーターがプロパティに適用されているかどうかを確認します。 プロパティが読み取り専用の場合、これに関する警告がブラウザーコンソールに表示され、値に変更はありません。そうでない場合、新しいデータが単に状態に書き込まれます。




したがって、別のコンポーネント内で使用される場合と、コンポーネントへのリンクを受け取り、オブジェクトとしてさらに操作する場合の両方で、Reactプロパティを介してコンポーネントにアクセスするためのユニバーサルAPIを取得しました。 そして、デコレータの助けを借りて、デコレータを使った作業は簡単で簡単です。



私たちのアプローチのデモンストレーションにより、誰かがコンポーネントを操作するためのAPIを簡素化できることを願っています。TypeScriptのデコレータの詳細を見るために、同僚の記事The Type Side of TypeScript-@ decorators with examplesをお勧めします



All Articles