HarmonyOS 学習ロード Ark 開発フレームワーク - ArkTS 言語の学習 (状態管理 2)

@Prop デコレータ: 親子の一方向同期

@Prop で修飾された変数は、親コンポーネントとの一方向の同期関係を確立できます。@Prop で修飾された変数は変更可能ですが、変更はその親コン​​ポーネントに同期されません。

概要

@Prop で修飾された変数は、親コンポーネントとの一方向の同期関係を確立します。

  • @Prop 変数を使用するとローカルでの変更が可能ですが、変更された変更は親コンポーネントに同期されません。
  • 親コンポーネントのデータ ソースが変更されると、関連する @Prop で装飾された変数が自動的に更新されます。子コンポーネントが @Prop 装飾された関連変数値をローカルに変更し、親コンポーネント内の対応する @State 装飾変数が変更された場合、子コンポーネントによってローカルに変更された @Prop 装飾された関連変数値は上書きされます。

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

@Prop変数デコレータ

説明する

デコレータパラメータ

なし

同期タイプ

一方向同期: 親コンポーネントの状態変数値の変更は、子コンポーネントの @Prop によって修飾された変数に同期され、子コンポーネントの @Prop 変数の変更は状態に同期されません。親コンポーネントの変数

装飾が可能な変数型

文字列、数値、ブール値、列挙型。

any はサポートされておらず、未定義および null も許可されません。

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

親コンポーネントでは、以下に示すように、@Prop 装飾に渡される値を未定義または null にすることはできません。

CompA ({ aProp: 未定義 })

CompA ({ aProp: null })

@Prop とデータ ソースの種類は同じである必要があります。次の 3 つの状況があります (データ ソースの例として @State を取り上げます)。

  • @Prop によって修飾された変数は、親コンポーネントの状態変数、つまり @Prop : S および @State : S と同じ型です。
  • 親コンポーネントの状態変数が配列の場合、 @Prop で修飾された変数は、親コンポーネントの状態変数の配列項目と同じ型になります。つまり、 @Prop : S および @State : Array<S> です。
  • 親コンポーネントの状態変数が Object または class の場合、 @Prop で修飾された変数は、親コンポーネントの状態変数のプロパティの型と同じになります。つまり、 @Prop : S および @State : { propA: S }

修飾された変数の初期値

ローカルの初期化を許可します。

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

パス/アクセス

説明する

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

ローカルで初期化がある場合はオプションです。親コンポーネントの通常の変数、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink、および @LocalStorageProp をサポートして子コンポーネントを初期化します。 @Prop変数。

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

@Prop は、サブコンポーネント内の通常の変数、@State、@Link、@Prop、@Provide の初期化解除をサポートします。

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

@Prop で装飾された変数はプライベートであり、コンポーネント内でのみアクセスできます。

図 1 初期化ルールの図

 

変化や行動を観察する

変化を観察する

@Prop で修飾されたデータでは、次の変化が観察されます。

  • 装飾のタイプが許可されたタイプの場合、つまり、文字列、数値、ブール値、および列挙型で観察できる割り当ての変更。
// 简单类型
@Prop count: number;
// 赋值的变化可以被观察到
this.count = 1;

@State と @Prop の同期シナリオの場合:

  • 子コンポーネントの @Prop 変数を、親コンポーネントの @State 変数の値で初期化します。@State 変数が変更されると、変数値も @Prop 変数に同期的に更新されます。
  • @Prop で装飾された変数を変更しても、そのデータ ソースの @State で装飾された変数の値には影響しません。
  • @State に加えて、データ ソースは @Link または @Prop で装飾することもでき、@Prop の同期メカニズムは同じです。
  • データソース変数と @Prop 変数は同じ型である必要があります。

フレームの動作

@Prop 変数値の初期化と更新のメカニズムを理解するには、@Prop 変数を使用した親コンポーネントと子コンポーネントの初期レンダリングと更新プロセスを理解する必要があります。

  1. 初期レンダリング:
    1. 親コンポーネントの build() 関数を実行すると、子コンポーネントの新しいインスタンスが作成され、データ ソースが子コンポーネントに渡されます。
    2. 子コンポーネントの @Prop で修飾された変数を初期化します。
  2. 更新する:
    1. 子コンポーネント @Prop が更新されると、更新は現在の子コンポーネントにのみ残り、親コンポーネントには同期されません。
    2. 親コンポーネントのデータ ソースが更新されると、子コンポーネントの @Prop 装飾変数は親コンポーネントのデータ ソースによってリセットされ、@Prop 装飾のすべてのローカル変更は親の更新によって上書きされます。成分。

使用するシーン

親コンポーネント @State から子コンポーネント @Prop への単純なデータ型の同期

次の例は、@State から子コンポーネント @Prop への単純なデータ同期です。親コンポーネント ParentComponent の状態変数 countDownStartValue は、子コンポーネント CountDownComponent 内の @Prop によって修飾されたカウントを初期化します。「再試行」をクリックします。カウントの変更は次のとおりです。 CountDownComponent 内にのみ保持され、親には同期されません。

ParentComponent の状態変数 countDownStartValue を変更すると、CountDownComponent のカウントがリセットされます。

@Component
struct CountDownComponent {
  @Prop count: number;
  costOfOneAttempt: number = 1;

  build() {
    Column() {
      if (this.count > 0) {
        Text(`You have ${this.count} Nuggets left`)
      } else {
        Text('Game over!')
      }
      // @Prop装饰的变量不会同步给父组件
      Button(`Try again`).onClick(() => {
        this.count -= this.costOfOneAttempt;
      })
    }
  }
}

@Entry
@Component
struct ParentComponent {
  @State countDownStartValue: number = 10;

  build() {
    Column() {
      Text(`Grant ${this.countDownStartValue} nuggets to play.`)
      // 父组件的数据源的修改会同步给子组件
      Button(`+1 - Nuggets in New Game`).onClick(() => {
        this.countDownStartValue += 1;
      })
      // 父组件的修改会同步给子组件
      Button(`-1  - Nuggets in New Game`).onClick(() => {
        this.countDownStartValue -= 1;
      })

      CountDownComponent({ count: this.countDownStartValue, costOfOneAttempt: 2 })
    }
  }
}

上の例では:

  1. CountDownComponent サブコンポーネントが最初に作成されるとき、@Prop によって修飾された count 変数は、親コンポーネント @State によって修飾された countDownStartValue 変数から初期化されます。
  2. 「+1」または「-1」ボタンを押すと、親コンポーネントの @State 装飾の countDownStartValue 値が変更され、親コンポーネントの再レンダリングがトリガーされます。親コンポーネントでは、countDownStartValue 状態変数を使用する UI コンポーネントが更新され、CountDownComponent サブコンポーネントのカウント値が一方向に同期的に更新されます。
  3. カウント状態変数の値を更新すると、CountDownComponent の再レンダリングもトリガーされます。再レンダリング プロセス中に、カウント状態変数を使用する if ステートメント条件 (this.count > 0) が評価され、関連する UI コンポーネントがtrue分岐内のcount状態変数への関数を実行する TextコンポーネントのUI表示を更新する記述。
  4. サブコンポーネント CountDownComponent の「再試行」ボタンが押されると、その @Prop 変数カウントが変更されますが、カウント値の変更は親コンポーネントの countDownStartValue 値には影響しません。
  5. 親コンポーネントの countDownStartValue 値が変更されると、親コンポーネントの変更により、子コンポーネント CountDownComponent のカウントのローカル変更が上書きされます。

親コンポーネント @State 配列項目と子コンポーネント @Prop の単純なデータ型の同期

@State が親コンポーネントで修飾されている場合、その配列項目も @Prop を初期化できます。次の例では、親コンポーネント Index の @State で修飾された配列 arr は、その配列項目を子コンポーネント Child の @Prop で修飾された値に初期化します。

@Component
struct Child {
  @Prop value: number;

  build() {
    Text(`${this.value}`)
      .fontSize(50)
      .onClick(()=>{this.value++})
  }
}

@Entry
@Component
struct Index {
  @State arr: number[] = [1,2,3];

  build() {
    Row() {
      Column() {
        Child({value: this.arr[0]})
        Child({value: this.arr[1]})
        Child({value: this.arr[2]})

        Divider().height(5)

        ForEach(this.arr, 
          item => {
            Child({value: item})
          }, 
          item => item.toString()
        )
        Text('replace entire arr')
        .fontSize(50)
        .onClick(()=>{
          // 两个数组都包含项“3”。
          this.arr = this.arr[0] == 1 ? [3,4,5] : [1,2,3];
        })
      }
    }
  }
}

最初のレンダリングでは 6 つのサブコンポーネント インスタンスが作成され、@Prop で装飾された変数の初期化ごとに配列項目がローカルにコピーされます。子コンポーネントの onclick イベント ハンドラーは、ローカル変数の値を変更します。

複数回クリックしたと仮定すると、すべての変数のローカル値は「7」になります。

7
7
7
----
7
7
7

「arr 全体を置換」をクリックすると、画面に次の情報が表示されます。なぜですか?

3
4
5
----
7
4
5
  • 子コンポーネント Child で行われたすべての変更は、親コンポーネント Index コンポーネントには同期されません。そのため、6 つのコンポーネントすべてが 7 として表示されていても、親コンポーネント Index では、this.arr によって保存された値は [1 のままです。 、2、3]。
  • arr 全体を置換をクリックすると、this.arr[0] == 1 が確立され、this.arr を [3, 4, 5] に割り当てます。
  • this.arr[0] が変更されたため、Child({value: this.arr[0]}) コンポーネントは this.arr[0] の更新をインスタンス @Prop 装飾変数に同期します。Child({value: this.arr[1]}) と Child({value: this.arr[2]}) についても同様です。
  • this.arr の変更により ForEach 更新がトリガーされ、this.arr の更新の前後に、値 3 を持つ配列項目 ([3, 4, 5] および [1, 2, 3]) が存在します。diffの仕組みにより、配列項目「3」は保持され、配列項目「1」と「2」が削除され、配列項目「4」と「5」が追加されます。これは、配列項目「3」のコンポーネントが再生成されず、最初の位置に移動されることを意味します。したがって、「3」に対応するコンポーネントは更新されませんが、このとき、「3」に対応するコンポーネントの値は「7」となり、ForEachの最終レンダリング結果は「7」、「4」、「4」となります。 「5」。

親コンポーネントの @State クラス オブジェクト プロパティから @Prop 単純型への同期

ライブラリに 1 冊の本と 2 人のユーザーがいる場合、各ユーザーは他の読者ユーザーに影響を与えることなく、その本を既読としてマークできます。コード的には、@Prop ブック オブジェクトに対するローカルの変更は、ライブラリ コンポーネントの @State ブック オブジェクトに同期されません。

class Book {
  public title: string;
  public pages: number;
  public readIt: boolean = false;

  constructor(title: string, pages: number) {
    this.title = title;
    this.pages = pages;
  }
}

@Component
struct ReaderComp {
  @Prop title: string;
  @Prop readIt: boolean;

  build() {
    Row() {
      Text(this.title)
      Text(`... ${this.readIt ? 'I have read' : 'I have not read it'}`)
        .onClick(() => this.readIt = true)
    }
  }
}

@Entry
@Component
struct Library {
  @State book: Book = new Book('100 secrets of C++', 765);

  build() {
    Column() {
      ReaderComp({ title: this.book.title, readIt: this.book.readIt })
      ReaderComp({ title: this.book.title, readIt: this.book.readIt })
    }
  }
}

@Prop のローカル初期化が親コンポーネントと同期されない

@Component によって修飾されたコンポーネントの再利用シナリオをサポートするために、@Prop はローカル初期化をサポートします。これにより、@Prop が親コンポーネントとの同期関係を確立するかどうかがオプションになります。@Prop のデータソースを親から子に渡すことは、@Prop にローカル初期化がある場合にのみオプションです。

以下の例では、子コンポーネントに 2 つの @Prop 変数が含まれています。

  • @Prop CustomCounter にはローカル初期化がないため、親コンポーネントは @Prop を初期化するためのデータ ソースを提供する必要があります。親コンポーネントのデータ ソースが変更されると、@Prop も更新されます。
  • @Prop CustomCounter2 にはローカル初期化があり、この場合でも、@Prop は親コンポーネントにデータ ソースを @Prop に同期させることを許可しますが、強制はしません。
@Component
struct MyComponent {
  @Prop customCounter: number;
  @Prop customCounter2: number = 5;

  build() {
    Column() {
      Row() {
        Text(`From Main: ${this.customCounter}`).width(90).height(40).fontColor('#FF0010')
      }

      Row() {
        Button('Click to change locally !').width(480).height(60).margin({ top: 10 })
          .onClick(() => {
            this.customCounter2++
          })
      }.height(100).width(480)

      Row() {
        Text(`Custom Local: ${this.customCounter2}`).width(90).height(40).fontColor('#FF0010')
      }
    }
  }
}

@Entry
@Component
struct MainProgram {
  @State mainCounter: number = 10;

  build() {
    Column() {
      Row() {
        Column() {
          Button('Click to change number').width(480).height(60).margin({ top: 10, bottom: 10 })
            .onClick(() => {
              this.mainCounter++
            })
        }
      }

      Row() {
        Column()       
          // customCounter必须从父组件初始化,因为MyComponent的customCounter成员变量缺少本地初始化;此处,customCounter2可以不做初始化。
          MyComponent({ customCounter: this.mainCounter })
          // customCounter2也可以从父组件初始化,父组件初始化的值会覆盖子组件customCounter2的本地初始化的值
          MyComponent({ customCounter: this.mainCounter, customCounter2: this.mainCounter })
        }.width('40%')
      }
    }
  }

おすすめ

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