HarmonyOS Learning Road Ark 開発フレームワーク - ArkTS 言語の学習 (状態管理 1)

状態管理の概要

前の説明では、構築したページのほとんどは静的インターフェイスでした。動的でインタラクティブなインターフェイスを構築したい場合は、「状態」の概念を導入する必要があります。

図 1 の レンダリング

 

上の例では、ユーザーのアプリケーションとの対話によりテキスト状態の変化がトリガーされ、これにより UI がレンダリングされ、UI が「Hello World」から「Hello ArkUI」に変わります。

宣言型 UI プログラミング フレームワークでは、UI はプログラム状態の実行結果であり、ユーザーはアプリケーションの実行時状態がパラメーターである UI モデルを構築します。パラメータが変更されると、UI も返された結果として対応する変更を加えます。これらの実行時の状態変化によってもたらされる UI の再レンダリングは、ArkUI の状態管理メカニズムと総称されます。

カスタム コンポーネントには変数があり、状態変数になるには変数をデコレータで装飾する必要があります。状態変数が変更されると UI レンダリングが更新されます。状態変数を使用しない場合、UI は初期化中にのみレンダリングされ、後で更新されません。以下の図は、State と View (UI) の関係を示しています。

  • ビュー (UI): UI レンダリングは、一般に、カスタム コンポーネントのビルド メソッドおよび @Builder によって装飾されたメソッド内の UI 記述を指します。
  • 状態: 状態は通常、デコレーターによって装飾されたデータを指します。ユーザーは、コンポーネントのイベント メソッドをトリガーして状態データを変更します。状態データが変更されると、UI が再レンダリングされます。

基本的な考え方

  • 状態変数: 状態デコレータによって装飾された変数。変更すると UI レンダリングが更新されます。
  • 通常の変数: 状態のない変数。通常は補助計算に使用されます。その変更によって UI が更新されることはありません。
  • データ ソース/同期ソース: さまざまな状態データに同期できる、状態変数の元のソース。これは通常、親コンポーネントから子コンポーネントに渡されるデータを意味します。
  • 名前付きパラメータ メカニズム: 親コンポーネントは、指定されたパラメータを子コンポーネントの状態変数に渡します。これは、親と子の同期パラメータを渡す主な手段です。例: CompA: ({ aProp: this.aProp })。
  • 親コンポーネントからの初期化: 親コンポーネントは、名前付きパラメーター メカニズムを使用して、指定されたパラメーターを子コンポーネントに渡します。親コンポーネントから値が渡された場合、ローカル初期化のデフォルト値は上書きされます。例:
@Component
struct MyComponent {
  @State count: number = 0;
  private increaseBy: number = 1;

  build() {
  }
}

@Component
struct Parent {
  build() {
    Column() {
      // 从父组件初始化,覆盖本地定义的默认值
      MyComponent({ count: 1, increaseBy: 2 })
    }
  }
}

 

  • サブノードの初期化: コンポーネント内の状態変数をサブコンポーネントに渡して、サブコンポーネントに対応する状態変数を初期化できます。上記と同じ例です。
  • ローカル初期化: 変数が宣言されると、初期化のデフォルト値として値が割り当てられます。例: @State カウント: 数値 = 0。

デコレータの概要

ArkUI にはさまざまなデコレータが用意されており、これらのデコレータを使用すると、状態変数はコンポーネント内の変更を観察できるだけでなく、親子コンポーネント、コンポーネント間のレベル、グローバルな変更など、異なるコンポーネント レベル間で受け渡すこともできます。状態変数の影響範囲に応じて、すべてのデコレーターは次のように大別できます。

  • コンポーネントの状態を管理するデコレータ: コンポーネント レベルの状態管理では、コンポーネント内の変更および異なるコンポーネント レベルの変更を監視できますが、同じコンポーネント ツリー、つまり同じページのみを監視する必要があります。
  • アプリケーションの状態を管理するデコレータ: アプリケーション レベルの状態管理。さまざまなページやさまざまな UIAbility の状態変化を監視でき、アプリケーション内のグローバルな状態管理です。

データ転送形式と同期タイプの観点から、デコレータは次のように分類することもできます。

  • 読み取り専用の片道パス。
  • 変更可能な双方向転送。

具体的なデコレータの紹介については、管理コンポーネントが持つ状態と管理アプリケーションが持つ状態を参照してください。開発者はこれらの機能を柔軟に活用して、データとUIの連携を実現できます。

 

上の図では、コンポーネント部分のデコレータがコンポーネントレベルの状態管理であり、アプリケーション部分がアプリケーションの状態管理です。開発者は @StorageLink/@LocalStorageLink と @StorageProp/@LocalStorageProp を使用して、アプリケーションとコンポーネントの状態の双方向および一方向の同期を実現できます。図の矢印の方向はデータの同期方向で、一重の矢印は一方向の同期、二重の矢印は双方向の同期です。

管理コンポーネントが所有する状態、つまり、図のコンポーネント レベルでの状態管理:

  • @State: @State によって修飾された変数には、それが属するコンポーネントの状態が含まれており、そのサブコンポーネントの一方向および双方向同期のためのデータ ソースとして使用できます。その値が変更されると、関連するコンポーネントのレンダリングが更新されます。
  • @Prop: @Prop で修飾された変数は、親コンポーネントとの一方向の同期関係を確立できます。@Prop で修飾された変数は変更可能ですが、変更は親コンポーネントに同期されません。
  • @Link: @Link で修飾された変数と親コンポーネントの状態変数は双方向の同期関係を構築し、親コンポーネントは @Link で修飾された変数の変更と親コンポーネントの更新の同期を受け入れます。 @Link で修飾された変数にも同期されます。
  • @Provide/@Consume: @Provide/@Consume で修飾された変数は、コンポーネント レベル (マルチレイヤー コンポーネント) 間で状態変数を同期するために使用され、パラメーター命名メカニズムを経由せずに、エイリアス (エイリアス) またはプロパティ名を通じてバインドできます。
  • @Observed: @Observed はクラスを装飾し、多層のネストされたシーンを観察する必要があるクラスは @Observed で装飾する必要があります。@Observed を単独で使用しても効果はないため、@ObjectLink および @Prop と組み合わせて使用​​する必要があります。
  • @ObjectLink: @ObjectLink で修飾された変数は、@Observed で修飾されたクラスのインスタンスを受け取ります。これは、マルチレイヤーのネストされたシーンを監視し、親コンポーネントのデータ ソースとの双方向同期を構築するために使用されます。

説明する

@Observed/@ObjectLink のみがネストされたシーンを観察でき、他の状態変数は最初のレイヤーのみを観察できます。詳細については、各デコレータの章の「変更と動作の観察」セクションを参照してください。

アプリケーションが所有する状態を管理します。つまり、図のアプリケーション レベルでの状態管理です。

  • AppStorage は、アプリケーション内の特別なシングルトン LocalStorage オブジェクトです。これはアプリケーション レベルのデータベースであり、プロセスにバインドされています。@StorageProp および @StorageLink デコレータを通じてコン​​ポーネントとリンクできます。
  • AppStorage はアプリケーション状態の「ハブ」です。コンポーネント (UI) と対話する必要があるデータは、永続データ PersistentStorage や環境変数Environment など、AppStorage に保存されます。次に、UI は、AppStorage が提供するデコレータまたは API インターフェイスを介してこれらのデータにアクセスします。
  • このフレームワークは LocalStorage も提供しており、AppStorage は LocalStorage の特別なシングルトンです。LocalStorage は、アプリケーションによって宣言されたアプリケーション状態のメモリ内の「データベース」です。通常、ページレベルの状態共有に使用されます。@LocalStorageProp および @LocalStorageLink デコレータは UI とリンクできます。

その他の状態管理機能

@Watch は、状態変数の変化を監視するために使用されます。

$$ 演算子: TS 変数と組み込みコンポーネントの内部状態が同期されるように、組み込みコンポーネントの TS 変数への参照を提供します。

コンポーネントが所有する状態を管理する

@State デコレータ: コンポーネント内の状態

@State で装飾された変数、つまり状態変数は、変数に状態プロパティが設定されると、カスタム コンポーネントのレンダリングにバインドされます。状態が変化すると、UI のレンダリングもそれに応じて変化します。

状態変数関連のデコレータの中で、@State は最も基本的なもので、変数に状態属性を持たせることができるデコレータであり、ほとんどの状態変数のデータ ソースでもあります。

概要

@State 装飾変数は、宣言パラダイムの他の装飾変数と同様、プライベートであり、コンポーネント内からのみアクセスでき、宣言時に型とローカル初期化を指定する必要があります。オプションで、名前付きパラメーター メカニズムを使用して、親コンポーネントから初期化を実行することもできます。

@State で修飾された変数には次の特性があります。

  • 一方向または双方向のデータ同期は、@State 装飾変数と、サブコンポーネント内の @Prop、@Link、または @ObjectLink 装飾変数の間で確立されます。
  • @State デコレーションの変数ライフ サイクルは、それが属するカスタム コンポーネントのライフ サイクルと同じです。

デコレータの使用ルールの説明

@State変数デコレータ

説明する

デコレータパラメータ

なし

同期タイプ

親コンポーネント内のいかなる種類の変数とも同期されません。

装飾が可能な変数型

オブジェクト、クラス、文字列、数値、ブール型、列挙型、およびこれらの型の配列。

タイプを指定する必要があります。

Any はサポートされず、単純型と複合型の共用体型はサポートされず、未定義と null は許可されません。

説明する

Date 型を修飾しないことをお勧めします。修飾すると、アプリケーションが異常な動作をする可能性があります。

Length、ResourceStr、および ResourceColor 型はサポートされていません。Length、ResourceStr、および ResourceColor は、単純型と複合型の共用体型です。

修飾された変数の初期値

を指定する必要があります。

変数の転送・アクセスルールの説明

パス/アクセス

説明する

親コンポーネントから初期化する

オプションで、親コンポーネントからまたはローカルで初期化します。

親コンポーネントの通常の変数、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink、および @LocalStorageProp で装飾された変数をサポートし、子コンポーネントの @State を初期化します。

子コンポーネントの初期化に使用されます

@State で装飾された変数は、サブコンポーネント、@State、@Link、@Prop、@Provide の通常の変数の初期化をサポートします。

コンポーネント外部へのアクセスをサポートするかどうか

サポートされていません。コンポーネント内でのみアクセス可能です。

図 1 初期化ルールの図

 

変化や行動を観察する

状態変数に対するすべての変更が UI を更新するわけではありません。フレームワークによって監視できる変更のみが UI を更新します。このセクションでは、どのような変更が観察されるのか、また、変更が観察された後にフレームワークがどのように UI を更新するか、つまりフレームワークの動作がどのようなものであるかを紹介します。

変化を観察する

  • 装飾データ型がブール型、文字列型、数値型の場合、値の変化を観察できます。
// for simple type
@State count: number = 0;
// value changing can be observed
this.count = 1;
  • 装飾されたデータ型がクラスまたはオブジェクトの場合、それ自体の割り当ての変更とその属性割り当ての変更、つまり Object.keys(observedObject) によって返されるすべての属性を観察できます。例としては以下のようなものがあります。

ClassA クラスと Model クラスを宣言します。

class ClassA {
  public value: string;

  constructor(value: string) {
    this.value = value;
  }
}

class Model {
  public value: string;
  public name: ClassA;
  constructor(value: string, a: ClassA) {
    this.value = value;
    this.name = a;
  }
}

@State 装飾のタイプは Model です

// class类型
@State title: Model = new Model('Hello', new ClassA('World'));

@State 修飾された変数への代入。

// class类型赋值
this.title = new Model('Hi', new ClassA('ArkUI'));

@State 装飾された変数のプロパティに値を割り当てます。

// class属性的赋值
this.title.value = 'Hi'

ネストされたプロパティへの割り当ては観察されません。

// 嵌套的属性赋值观察不到
this.title.name.value = 'ArkUI'
  • 装飾されたオブジェクトが配列の場合、配列自体の割り当てと、配列の追加、削除、更新の変更を観察できます。例としては以下のようなものがあります。

ClassA クラスと Model クラスを宣言します。

class Model {
  public value: number;
  constructor(value: number) {
    this.value = value;
  }
}

@Stateで装飾されたオブジェクトがModel型の配列の場合。

@State title: Model[] = [new Model(11), new Model(1)]

配列自体の割り当ては監視可能です。

this.title = [new Model(2)]

配列項目の割り当てを確認できます。

this.title[0] = new Model(2)

配列項目が削除されていることがわかります。

this.title.pop()

追加された配列項目が確認できます。

this.title.push(new Model(12))

フレームの動作

  • 状態変数が変更されると、その状態変数に依存するコンポーネントをクエリします。
  • 状態変数に依存するコンポーネントの update メソッドを実行すると、コンポーネントはレンダリングを更新します。
  • この状態変数に関連しないコンポーネントまたは UI の説明は再レンダリングされないため、ページのレンダリングはオンデマンドで更新できます。

使用するシーン

単純型の変数の装飾

次の例は、単純なタイプの @State 装飾です。count は状態変数として @State によって装飾され、count の変更によりボタン コンポーネントが更新されます。

  • 状態変数 count が変化すると、Button コンポーネントのみがそれに関連付けられていることがわかります。
  • オンデマンド更新を実現するには、Buttonコンポーネントのupdateメソッドを実行します。
@Entry
@Component
struct MyComponent {
  @State count: number = 0;

  build() {
    Button(`click times: ${this.count}`)
      .onClick(() => {
        this.count += 1;
      })
  }
}

クラスオブジェクト型の変数を装飾する

  • カスタム コンポーネント MyComponent は、@State によって装飾された状態変数 count と title を定義します。ここで、タイトルのタイプはカスタム クラス Model です。count または title の値が変更された場合は、MyComponent の状態変数を使用して UI コンポーネントをクエリし、再レンダリングします。

  • EntryComponent には複数の MyComponent コンポーネント インスタンスがあり、最初の MyComponent の内部状態が変更されても 2 番目の MyComponent には影響しません。

class Model {
  public value: string;

  constructor(value: string) {
    this.value = value;
  }
}

@Entry
@Component
struct EntryComponent {
  build() {
    Column() {
      // 此处指定的参数都将在初始渲染时覆盖本地定义的默认值,并不是所有的参数都需要从父组件初始化
      MyComponent({ count: 1, increaseBy: 2 })
      MyComponent({ title: new Model('Hello, World 2'), count: 7 })
    }
  }
}

@Component
struct MyComponent {
  @State title: Model = new Model('Hello World');
  @State count: number = 0;
  private increaseBy: number = 1;

  build() {
    Column() {
      Text(`${this.title.value}`)
      Button(`Click to change title`).onClick(() => {
        // @State变量的更新将触发上面的Text组件内容更新
        this.title.value = this.title.value === 'Hello ArkUI' ? 'Hello World' : 'Hello ArkUI';
      })

      Button(`Click to increase count=${this.count}`).onClick(() => {
        // @State变量的更新将触发该Button组件的内容更新
        this.count += this.increaseBy;
      })
    }
  }
}

この例から、@State 変数の最初のレンダリングの初期化プロセスを理解できます。

  • デフォルトのローカル初期化を使用します。 
@State title: Model = new Model('Hello World');
@State count: number = 0;
  • @State の場合、名前付きパラメータ メカニズムによって渡される値は必須ではありません。名前付きパラメータが渡されない場合は、ローカル初期化のデフォルト値が使用されます。 
MyComponent({ count: 1, increaseBy: 2 })

おすすめ

転載: blog.csdn.net/weixin_47094733/article/details/131909258