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

State Management Overview

In the previous description, most of the pages we built were static interfaces. If you want to build a dynamic and interactive interface, you need to introduce the concept of "state".

Figure 1  rendering

 

In the example above, the user's interaction with the application triggers a text state change, which causes the UI to render, changing the UI from "Hello World" to "Hello ArkUI".

In the declarative UI programming framework, the UI is the running result of the program state, and the user builds a UI model, in which the runtime state of the application is a parameter. When the parameters are changed, the UI will also make corresponding changes as the returned result. The re-rendering of the UI brought about by these runtime state changes is collectively referred to as the state management mechanism in ArkUI.

Custom components have variables, and variables must be decorated with decorators to become state variables. Changes in state variables will cause UI rendering to refresh. If you do not use state variables, the UI can only be rendered during initialization, and will not be refreshed later. The figure below shows the relationship between State and View (UI).

  • View (UI): UI rendering, generally refers to the UI description in the build method of the custom component and the method decorated by @Builder.
  • State: State, generally refers to the data decorated by the decorator. The user changes the state data by triggering the event method of the component. A change in state data causes the UI to re-render.

basic concept

  • State variables: Variables decorated by state decorators, changes will cause UI rendering updates.
  • Regular variables: Variables without state, usually used for auxiliary calculations. Its changes never cause a refresh of the UI.
  • Data source/synchronization source: the original source of state variables, which can be synchronized to different state data. It usually means the data passed from the parent component to the child component.
  • Named parameter mechanism: The parent component passes the specified parameter to the state variable of the child component, which is the main means of passing synchronization parameters for the parent and child. Example: CompA: ({ aProp: this.aProp }).
  • Initialization from the parent component: The parent component uses the named parameter mechanism to pass the specified parameters to the child component. The default value of local initialization will be overwritten when there is a value passed from the parent component. Example:
@Component
struct MyComponent {
  @State count: number = 0;
  private increaseBy: number = 1;

  build() {
  }
}

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

 

  • Initialize sub-nodes: The state variables in the component can be passed to the sub-components to initialize the state variables corresponding to the sub-components. Same example as above.
  • Local initialization: When the variable is declared, it is assigned a value as the default value for initialization. Example: @State count: number = 0.

Overview of decorators

ArkUI provides a variety of decorators. By using these decorators, state variables can not only observe changes within components, but also be passed between different component levels, such as parent-child components, cross-component levels, and global changes. . According to the scope of influence of state variables, all decorators can be roughly divided into:

  • The decorator that manages the state of the component: Component-level state management can observe changes in the component and changes in different component levels, but only need to observe the same component tree, that is, the same page.
  • Decorator that manages the state of the application: application-level state management, which can observe the state changes of different pages or even different UIAbilities, and is a global state management within the application.

From the perspective of data transfer form and synchronization type, decorators can also be divided into:

  • read-only one-way pass;
  • Changeable two-way transfer.

The diagram is as follows. For the introduction of specific decorators, see the state owned by the management component and the state owned by the management application. Developers can flexibly use these capabilities to realize the linkage between data and UI.

 

In the figure above, the decorator in the Components part is the component-level state management, and the Application part is the state management of the application. Developers can use @StorageLink/@LocalStorageLink and @StorageProp/@LocalStorageProp to realize two-way and one-way synchronization of application and component states. The direction of the arrow in the figure is the data synchronization direction, the single arrow is the one-way synchronization, and the double arrow is the two-way synchronization.

The state owned by the management component, that is, the state management at the Components level in the figure:

  • @State: The variable decorated by @State has the state of the component it belongs to, and can be used as a data source for one-way and two-way synchronization of its subcomponents. When its value changes, it will cause the rendering refresh of related components.
  • @Prop: Variables decorated with @Prop can establish a one-way synchronization relationship with the parent component. Variables decorated with @Prop are mutable, but the modification will not be synchronized back to the parent component.
  • @Link: The variable decorated by @Link and the state variable of the parent component build a two-way synchronization relationship. The parent component will accept the synchronization of the modification of the variable decorated by @Link, and the update of the parent component will also be synchronized to the variable decorated by @Link.
  • @Provide/@Consume: Variables decorated with @Provide/@Consume are used to synchronize state variables across component levels (multi-layer components), and can be bound through alias (alias) or property name without passing through the parameter naming mechanism.
  • @Observed: @Observed decorates the class, and the class that needs to observe multi-layer nested scenes needs to be decorated with @Observed. Using @Observed alone has no effect and needs to be used in conjunction with @ObjectLink and @Prop.
  • @ObjectLink: The variable decorated with @ObjectLink receives an instance of the class decorated with @Observed, which is used to observe multi-layer nested scenes and build two-way synchronization with the data source of the parent component.

illustrate

Only @Observed/@ObjectLink can observe nested scenes, and other state variables can only observe the first layer. For details, see the "Observation of Changes and Behaviors" section of each decorator chapter.

Manage the state owned by the application, that is, the state management at the Application level in the figure:

  • AppStorage is a special singleton LocalStorage object in the application. It is an application-level database and bound to the process. It can be linked with components through @StorageProp and @StorageLink decorators.
  • AppStorage is the "hub" of the application state. Data that needs to interact with components (UI) is stored in AppStorage, such as persistent data PersistentStorage and environment variables Environment. The UI then accesses these data through the decorator or API interface provided by AppStorage;
  • The framework also provides LocalStorage, and AppStorage is a special singleton of LocalStorage. LocalStorage is the in-memory "database" of the application state declared by the application. It is usually used for page-level state sharing. The @LocalStorageProp and @LocalStorageLink decorators can be linked with the UI.

Other state management functions

@Watch is used to monitor changes in state variables.

$$ operator: Provide a reference to the TS variable for the built-in component, so that the TS variable and the internal state of the built-in component are kept in sync.

Manage state owned by components

@State decorator: state inside the component

Variables decorated with @State, or state variables, are bound to the rendering of the custom component once the variable has a state property. When the state changes, the UI will have a corresponding rendering change.

Among state variable-related decorators, @State is the most basic, a decorator that enables variables to have state attributes, and it is also the data source for most state variables.

overview

@State decorated variables, like other decorated variables in the declarative paradigm, are private and can only be accessed from within the component, and must specify their type and local initialization when declaring. Initialization can also optionally be done from the parent component using the named parameter mechanism.

Variables decorated with @State have the following characteristics:

  • One-way or two-way data synchronization is established between @State decorated variables and @Prop, @Link or @ObjectLink decorated variables in subcomponents.
  • The variable life cycle of @State decoration is the same as the life cycle of the custom component it belongs to.

Description of decorator usage rules

@State variable decorator

illustrate

decorator parameter

none

Synchronization type

Not synchronized with variables of any kind in the parent component.

Variable types that allow decoration

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

Type must be specified.

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

illustrate

It is recommended not to decorate the Date type, and the application may produce abnormal behavior.

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.

Explanation of variable transfer/access rules

pass/access

illustrate

Initialize from parent component

Optional, initialize from parent component or locally.

Supports regular variables in parent components, @State, @Link, @Prop, @Provide, @Consume, @ObjectLink, @StorageLink, @StorageProp, @LocalStorageLink and @LocalStorageProp decorated variables, and initializes @State of child components.

Used to initialize child components

Variables decorated with @State support initialization of regular variables of subcomponents, @State, @Link, @Prop, @Provide.

Whether to support access outside the component

Not supported, only accessible within components.

Figure 1  Illustration of initialization rules

 

Observe changes and behavior

Not all changes to state variables will cause the UI to refresh, only modifications that can be observed by the framework will cause the UI to refresh. This section introduces what kind of modification can be observed, and how the framework causes the UI to refresh after the change is observed, that is, what the behavior of the framework is.

observe changes

  • When the decorated data type is boolean, string, number type, you can observe the change of the value.
// for simple type
@State count: number = 0;
// value changing can be observed
this.count = 1;
  • When the decorated data type is class or Object, you can observe the change of its own assignment and the change of its attribute assignment, that is, all the attributes returned by Object.keys(observedObject). Examples are as follows.

Declare ClassA and Model classes.

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;
  }
}

The type of @State decoration is Model

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

Assignment to @State decorated variables.

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

Assign values ​​to properties of @State decorated variables.

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

Assignments to nested properties are not observed.

// 嵌套的属性赋值观察不到
this.title.name.value = 'ArkUI'
  • When the decorated object is an array, you can observe the assignment of the array itself and the changes of adding, deleting, and updating the array. Examples are as follows.

Declare ClassA and Model classes.

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

When the object decorated by @State is an array of Model type.

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

The assignment of the array itself is observable.

this.title = [new Model(2)]

The assignment of array items can be observed.

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

Removing an array item can be observed.

this.title.pop()

Added array items can be observed.

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

frame behavior

  • When the state variable is changed, query the components that depend on the state variable;
  • Execute the update method of the component that depends on the state variable, and the component updates the rendering;
  • Components or UI descriptions that are not related to this state variable will not be re-rendered, so that page rendering can be updated on demand.

scenes to be used

Decorating variables of simple types

The following example is a simple type of @State decoration, count is decorated by @State as a state variable, and the change of count causes the button component to be refreshed:

  • When the state variable count changes, it is found that only the Button component is associated with it;
  • Execute the update method of the Button component to realize on-demand refresh.
@Entry
@Component
struct MyComponent {
  @State count: number = 0;

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

Decorate variables of class object type

  • The custom component MyComponent defines the state variables count and title decorated by @State, where the type of title is the custom class Model. If the value of count or title changes, query the UI component using the state variable in MyComponent and re-render.

  • There are multiple MyComponent component instances in EntryComponent, and changes in the internal state of the first MyComponent will not affect the second 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;
      })
    }
  }
}

From this example, we can understand the initialization process of the @State variable's first rendering:

  • Use default local initialization: 
@State title: Model = new Model('Hello World');
@State count: number = 0;
  • For @State, the value passed by the named parameter mechanism is not mandatory. If there is no named parameter passed value, the default value of local initialization is used 
MyComponent({ count: 1, increaseBy: 2 })

Guess you like

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