HarmonyOS Learning Road Ark Development Framework - Learning ArkTS Language (State Management 3)

@Link decorator: parent-child two-way synchronization

The variable decorated with @Link in the child component establishes two-way data binding with the corresponding data source in the parent component.

overview

A variable decorated with @Link shares the same value as its data source in its parent component.

Description of decorator usage rules

@Link variable decorator

illustrate

decorator parameters

none

Synchronization type

Two-way synchronization.

@State, @StorageLink and @Link in the parent component and @Link in the child component can establish two-way data synchronization, and vice versa.

Variable types that allow decoration

Object, class, string, number, boolean, enum types, and arrays of these types.

The type must be specified and is the same as the type of the two-way binding state variable.

Any is not supported, union types of simple types and complex types are not supported, and undefined and null are not allowed.

illustrate

The Length, ResourceStr, and ResourceColor types are not supported, and Length, ResourceStr, and ResourceColor are union types of simple types and complex types.

The initial value of the decorated variable

None, disables local initialization.

Explanation of variable transfer/access rules

pass/access

illustrate

Initialize and update from parent component

required. Create two-way bindings with parent components @State, @StorageLink and @Link. Allow @State, @Link, @Prop, @Provide, @Consume, @ObjectLink, @StorageLink, @StorageProp, @LocalStorageLink and @LocalStorageProp decoration variables in the parent component to initialize the child component @Link.

Starting from API version 9, the @Link child component initializes @State from the parent component with the syntax Comp({ aLink: this.aState }). Comp({aLink: $aState}) is also supported.

Used to initialize child components

Allowed, can be used to initialize regular variables, @State, @Link, @Prop, @Provide.

Whether to support access outside the component

Private and can only be accessed within the owning component.

Figure 1  Illustration of initialization rules

Observe changes and behavior

observe changes

  • When the data type of the decoration is boolean, string, or number, the change of the value can be observed synchronously.
  • When the decorated data type is class or Object, you can observe the changes of assignment and attribute assignment, that is, all attributes returned by Object.keys(observedObject).
  • When the decorated object is an array, you can observe the changes of adding, deleting, and updating array cells in the array.

frame behavior

Variables decorated with @Link share a lifecycle with the custom component they describe.

In order to understand the @Link variable initialization and update mechanism, it is necessary to understand the relationship between the parent component and the child component that owns the @Link variable, the process of initial rendering and bidirectional update (take the parent component as @State as an example).

  1. Initial rendering: A new instance of the child component will be created after executing the build() function of the parent component. The initialization process is as follows:
    1. The @State variable in the parent component must be specified to initialize the @Link variable of the child component. The @Link variable value of the child component is kept in sync with the data source variable of its parent component (two-way data synchronization).
    2. The @State state variable wrapper class of the parent component is passed to the child component through the constructor. After the @Link wrapper class of the child component gets the @State state variable of the parent component, it registers the current @Link wrapper class this pointer to the @Link wrapper class of the parent component. State variables.
  2. The update of the data source of @Link: that is, the update of the state variable in the parent component causes the update of @Link of the related child component. Processing steps:
    1. Through the steps of initial rendering, we can see that the child component @Link wrapper class registers the current this pointer to the parent component. After the @State variable of the parent component is changed, it will traverse and update all system components (elementid) and state variables (such as @Link wrapper classes) that depend on it.
    2. After notifying the update of the @Link wrapper class, all system components (elementId) that depend on the @Link state variable in the subcomponent will be notified of the update. In this way, the state data synchronization of the parent component to the child component is realized.
  3. Update of @Link: When @Link in the child component is updated, the processing steps are as follows (take the parent component as @State as an example):
    1. After @Link is updated, call the set method of the @State wrapper class of the parent component to synchronize the updated value back to the parent component.
    2. The child component @Link and the parent component @State respectively traverse the dependent system components and update the corresponding UI. In this way, the child component @Link is synchronized back to the parent component @State.

scenes to be used

@Link for simple types and object-like types

In the following example, click "Parent View: Set yellowButton" and "Parent View: Set GreenButton" in the parent component ShufflingContainer to synchronize the changes from the parent component to the child components, and the changes of @Link decoration variables in the child components GreenButton and YellowButton are also Will be synchronized to its parent component.

class GreenButtonState {
  width: number = 0;
  constructor(width: number) {
    this.width = width;
  }
}
@Component
struct GreenButton {
  @Link greenButtonState: GreenButtonState;
  build() {
    Button('Green Button')
      .width(this.greenButtonState.width)
      .height(150.0)
      .backgroundColor('#00ff00')
      .onClick(() => {
        if (this.greenButtonState.width < 700) {
          // 更新class的属性,变化可以被观察到同步回父组件
          this.greenButtonState.width += 125;
        } else {
          // 更新class,变化可以被观察到同步回父组件
          this.greenButtonState = new GreenButtonState(100);
        }
      })
  }
}
@Component
struct YellowButton {
  @Link yellowButtonState: number;
  build() {
    Button('Yellow Button')
      .width(this.yellowButtonState)
      .height(150.0)
      .backgroundColor('#ffff00')
      .onClick(() => {
        // 子组件的简单类型可以同步回父组件
        this.yellowButtonState += 50.0;
      })
  }
}
@Entry
@Component
struct ShufflingContainer {
  @State greenButtonState: GreenButtonState = new GreenButtonState(300);
  @State yellowButtonProp: number = 100;
  build() {
    Column() {
      // 简单类型从父组件@State向子组件@Link数据同步
      Button('Parent View: Set yellowButton')
        .onClick(() => {
          this.yellowButtonProp = (this.yellowButtonProp < 700) ? this.yellowButtonProp + 100 : 100;
        })
      // class类型从父组件@State向子组件@Link数据同步
      Button('Parent View: Set GreenButton')
        .onClick(() => {
          this.greenButtonState.width = (this.greenButtonState.width < 700) ? this.greenButtonState.width + 100 : 100;
        })
      // class类型初始化@Link
      GreenButton({ greenButtonState: $greenButtonState })
      // 简单类型初始化@Link
      YellowButton({ yellowButtonState: $yellowButtonProp })
    }
  }
}

 @Link of array type

@Component
struct Child {
  @Link items: number[];

  build() {
    Column() {
      Button(`Button1: push`).onClick(() => {
        this.items.push(this.items.length + 1);
      })
      Button(`Button2: replace whole item`).onClick(() => {
        this.items = [100, 200, 300];
      })
    }
  }
}

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

  build() {
    Column() {
      Child({ items: $arr })
      ForEach(this.arr,
        item => {
          Text(`${item}`)
        },
        item => item.toString()
      )
    }
  }
}

As mentioned above, the ArkUI framework can observe the addition, removal and replacement of array elements. In this example, the types of @State and @Link are the same number[], it is not allowed to define @Link as a number type (@Link item: number), and create it with each data item in the @State array in the parent component Subassembly.

@Provide decorator and @Consume decorator: two-way synchronization with descendant components

@Provide and @Consume are applied to two-way data synchronization with descendant components, and are applied to scenarios where state data is passed between multiple levels. Unlike the above-mentioned passing between parent and child components through the named parameter mechanism, @Provide and @Consume get rid of the shackles of the parameter passing mechanism and realize cross-level passing.

The variable decorated with @Provide is in the ancestor node, which can be understood as a state variable that is "provided" to descendants. The variable decorated with @Consume is to "consume (bind)" the variable provided by the ancestor node in the descendant component.

overview

State variables decorated with @Provide/@Consume have the following characteristics:

  • The state variable decorated by @Provide is automatically available to all its descendant components, that is, the variable is "provided" to his descendant components. It can be seen that the convenience of @Provide is that developers do not need to pass variables between components multiple times.
  • The descendants use @Consume to obtain the variables provided by @Provide, and establish a two-way data synchronization between @Provide and @Consume. Unlike @State/@Link, the former can be passed between multi-level parent-child components.
  • @Provide and @Consume can be bound by the same variable name or the same variable alias, and the variable types must be the same.
// 通过相同的变量名绑定
@Provide a: number = 0;
@Consume a: number;

// 通过相同的变量别名绑定
@Provide('a') b: number = 0;
@Consume('a') c: number;

Decorator Description

The rules of @State also apply to @Provide, the difference is that @Provide also acts as a synchronization source for multiple descendants.

@Provide variable decorator

illustrate

decorator parameters

alias: const string, optional.

If an alias is specified, the variable is bound by the alias; if no alias is specified, the variable is bound by the variable name.

Synchronization type

Two-way synchronization.

Data synchronization from @Provide variables to all @Consume variables and vice versa. The operation of two-way synchronization is the same as the combination of @State and @Link.

Variable types that allow decoration

Object, class, string, number, boolean, enum types, and arrays of these types.

Any is not supported, union types of simple types and complex types are not supported, and undefined and null are not allowed.

Type must be specified. The @Consume variable must be of the same type as the @Provide variable.

illustrate

The Length, ResourceStr, and ResourceColor types are not supported, and Length, ResourceStr, and ResourceColor are union types of simple types and complex types.

The initial value of the decorated variable

must be specified.

@Consume variable decorator

illustrate

decorator parameters

alias: const string, optional.

If an alias is provided, the @Provide variable must have the same alias to match successfully; otherwise, the variable name must be the same to match successfully.

Synchronization type

Bidirectional: from @Provide variables (see @Provide for details) to all @Consume variables, and vice versa. Two-way synchronization operates the same as the combination of @State and @Link.

Variable types that allow decoration

Object, class, string, number, boolean, enum types, and arrays of these types.

any is not supported, and undefined and null are not allowed.

Type must be specified. The @Consume variable must be of the same type as the @Provide variable.

illustrate

  • Variables decorated with @Consume must have corresponding attributes and aliases of variables decorated with @Provide on their parent or ancestor nodes.

The initial value of the decorated variable

None, disables local initialization.

Explanation of variable transfer/access rules

@Provide pass/access

illustrate

Initialize and update from parent component

可选,允许父组件中常规变量、@State、@Link、@Prop、@Provide、@Consume、@ObjectLink、@StorageLink、@StorageProp、@LocalStorageLink和@LocalStorageProp装饰的变量装饰变量初始化子组件@Provide。

用于初始化子组件

允许,可用于初始化@State、@Link、@Prop、@Provide。

和父组件同步

否。

和后代组件同步

和@Consume双向同步。

是否支持组件外访问

私有,仅可以在所属组件内访问。

图1 @Provide初始化规则图示

@Consume传递/访问

说明

从父组件初始化和更新

禁止。通过相同的变量名和alias(别名)从@Provide初始化。

用于初始化子组件

允许,可用于初始化@State、@Link、@Prop、@Provide。

和祖先组件同步

和@Provide双向同步。

是否支持组件外访问

私有,仅可以在所属组件内访问

图2 @Consume初始化规则图示

观察变化和行为表现

观察变化

  • 当装饰的数据类型为boolean、string、number类型时,可以观察到数值的变化。
  • 当装饰的数据类型为class或者Object的时候,可以观察到赋值和属性赋值的变化(属性为Object.keys(observedObject)返回的所有属性)。
  • 当装饰的对象是array的时候,可以观察到数组的添加、删除、更新数组单元。

框架行为

  1. 初始渲染:
    1. @Provide装饰的变量会以map的形式,传递给当前@Provide所属组件的所有子组件;
    2. 子组件中如果使用@Consume变量,则会在map中查找是否有该变量名/alias(别名)对应的@Provide的变量,如果查找不到,框架会抛出JS ERROR;
    3. 在初始化@Consume变量时,和@State/@Link的流程类似,@Consume变量会保存在map中查找到的@Provide变量,并把自己注册给@Provide。
  2. 当@Provide装饰的数据变化时:
    1. 通过初始渲染的步骤可知,子组件@Consume已把自己注册给父组件。父组件@Provide变量变更后,会遍历更新所有依赖它的系统组件(elementid)和状态变量(@Consume);
    2. 通知@Consume更新后,子组件所有依赖@Consume的系统组件(elementId)都会被通知更新。以此实现@Provide对@Consume状态数据同步。
  3. 当@Consume装饰的数据变化时:
    1. 通过初始渲染的步骤可知,子组件@Consume持有@Provide的实例。在@Consume更新后调用@Provide的更新方法,将更新的数值同步回@Provide,以此实现@Consume向@Provide的同步更新。

使用场景

在下面的示例是与后代组件双向同步状态@Provide和@Consume场景。当分别点击CompA和CompD组件内Button时,reviewVotes 的更改会双向同步在CompA和CompD中。

@Component
struct CompD {
  // @Consume装饰的变量通过相同的属性名绑定其祖先组件CompA内的@Provide装饰的变量
  @Consume reviewVotes: number;

  build() {
    Column() {
      Text(`reviewVotes(${this.reviewVotes})`)
      Button(`reviewVotes(${this.reviewVotes}), give +1`)
        .onClick(() => this.reviewVotes += 1)
    }
    .width('50%')
  }
}

@Component
struct CompC {
  build() {
    Row({ space: 5 }) {
      CompD()
      CompD()
    }
  }
}

@Component
struct CompB {
  build() {
    CompC()
  }
}

@Entry
@Component
struct CompA {
  // @Provide装饰的变量reviewVotes由入口组件CompA提供其后代组件
  @Provide reviewVotes: number = 0;

  build() {
    Column() {
      Button(`reviewVotes(${this.reviewVotes}), give +1`)
        .onClick(() => this.reviewVotes += 1)
      CompB()
    }
  }
}

Guess you like

Origin blog.csdn.net/weixin_47094733/article/details/132034371