harmony 鸿蒙\@Provider and \@Consumer Decorators: Synchronizing Across Component Levels in a Two-Way Manner

  • 2025-06-12
  • 浏览 (6)

\@Provider and \@Consumer Decorators: Synchronizing Across Component Levels in a Two-Way Manner

\@Provider and \@Consumer are used for synchronizing data across the component levels in a two-way manner, so that you are free from the constraints of the component levels. \@Provider and \@Consumer are decorators in state management V2, so they can be used only in \@ComponentV2. A compilation error will be reported if they are used in \@Component.

Before reading this topic, you are advised to read \@ComponentV2.

NOTE

\@Provider and \@Consumer decorators are supported since API version 12.

Overview

\@Provider provides data. Its child components can use the \@Consumer to obtain the data by binding the same key. \@Consumer obtains data. It can obtain the \@Provider data of the nearest parent node by binding the same key. If the \@Provider data cannot be found, the local default value will be used. Data types decorated by \@Provider and \@Consumer must be the same.

The following notes must be paid attention to when using \@Provider and \@Consumer: - \@Provider and \@Consumer strongly depend on the custom component levels. \@Consumer is initialized to different values because the parent component of the custom component is different. - Using \@Provider with \@Consumer is equivalent to bonding components together. From the perspective of independent component, usage of \@Provider and \@Consumer should be lessened.

Capability Comparison: \@Provider and \@Consumer Vs. \@Provide and \@Consume

In state management V1, \@Provide and \@Consume are the decorators which provide two-way synchronization across component levels. This topic introduces \@Provider and \@Consumer decorators in state management V2. Although the names and features of the two pairs are similar, there are still some differences. If you are not familiar with \@Provide and \@Consume in state management V1, please skip this section.

Capability \@Provider and \@Consumer Decorators of V2 \@Provide and \@Consume Decorators of V1
\@Consume® Local initialization is allowed. Local default value will be used when \@Provider is not found. Local initialization is forbidden. An exception will be thrown when the \@Provide is not found.
Supported type function is supported. function is not supported.
Observation capability Only the value change of itself can be observed. To observe the nesting scenario, use this decorator together with \@Trace. Changes at the first layer can be observed. To observe the nesting scenario, use this decorator together with \@Observed and \@ObjectLink.
alias and attribute name alias is the unique matching key and the default attribute name. If both the alias and attribute name are key, the former one is matched first. If no match is found, the attribute name can be matched.
\@Provide® initialization from the parent component Forbidden. Allowed.
\@Provide® overloading support Enabled by default. That is, \@Provider can have duplicate names and \@Consumer can search upwards for the nearest \@Provider. Disabled by default. That is, \@Provide with duplicate names is not allowed in the component tree. If overloading is required, set allowOverride.

Decorator Description

Basic Rules

\@Provider syntax: @Provider(alias?: string) varName : varType = initValue

\@Provider Property Decorator Description
Decorator parameters aliasName?: string: alias. The default value is the attribute name.
Supported type Member variables in the custom component.
Property types include number, string, boolean, class, Array, Date, Map, and Set.
Arrow function.
Initialization from the parent component Forbidden.
Local initialization Required.
Observation capability Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Consumer.

\@Consumer syntax: @Consumer(alias?: string) varName : varType = initValue

\@Consumer Property Decorator Description
Decorator parameters aliasName?: string: alias. The default value is the attribute name. The nearest \@Provider is searched upwards.
Supported type Member variables in the custom component.
Property types include number, string, boolean, class, Array, Date, Map, and Set.
Arrow function.
Initialization from the parent component Forbidden.
Local initialization Required.
Observation capability Be equivalent to \@Trace. Changes will be synchronized to the corresponding \@Provider.

aliasName and Attribute Name

\@Provider and \@Consumer accept the optional parameter aliasName. If the parameter is not set, the attribute name will be used as the default aliasName Note that aliasName is the unique key used to match \@Provider and \@Consumer.

The following three examples clearly describe how \@Provider and \@Consumer use aliasName for searching and matching.

@ComponentV2
struct Parent {
  // The aliasName is not defined. Use the property name "str" as the aliasName.
  @Provider() str: string = 'hello';
}

@ComponentV2
struct Child {
  // Define aliasName as"str" and use it to search.
  // If the value can be found on the Parent component, use the value "hello" of @Provider.
  @Consumer('str') str: string = 'world';
}
@ComponentV2
struct Parent {
  // Define aliasName as "alias".
  @Provider('alias') str: string = 'hello';
}

@ComponentV2 struct Child {
  // Define aliasName as "alias", find out @Provider, and obtain the value "hello".
  @Consumer('alias') str: string = 'world';
}
@ComponentV2
struct Parent {
  // Define aliasName as "alias".
  @Provider('alias') str: string = 'hello';
}

@ComponentV2
struct Child {
  // The aliasName is not defined. Use the property name "str" as the aliasName.
  // The corresponding @Provider is not found, use the local value "world".
  @Consumer() str: string = 'world';
}

Variable Passing

Passing Rules Description
Initialization from the parent component Variables decorated by \@Provider and \@Consumer can only be initialized locally.
Child component initialization Variables decorated by \@Provider and \@Consumer can be used to initialize \@Param decorated variables in the child component.

Constraints

  1. \@Provider and \@Consumer are property decorators for custom components. They can only decorate the attributes of custom components and cannot decorate the class properties.
  2. \@Provider and \@Consumer are decorators of the state management V2, which can be used only in \@ComponentV2 but not in \@Component.
  3. \@Provider and \@Consumer support only local initialization.

Use Scenarios

Synchronizing \@Provider and \@Consumer in a Two-Way Manner

Establishing a Two-Way Binding

  1. Initialize the Parent and Child custom components:
    • @Consumer() str: string = ‘world’ in the Child component searches upwards to find @Provider() str: string = ‘hello’ in the Parent component.
    • @Consumer() str: string = ‘world’ is initialized to the value of @Provider, that is, ‘hello’.
    • Both of them establish a two-way synchronization relationship.
  2. Click the button in Parent to change the \@Provider decorated str and notify the corresponding \@Consumer to re-render the UI.
  3. Click the button in Child to change the \@Consumer decorated str, and notify the corresponding \@Provider to re-render the UI.
@Entry
@ComponentV2
struct Parent {
  @Provider() str: string = 'hello';

  build() {
    Column() {
      Button(this.str)
        .onClick(() => {
          this.str += '0';
        })
      Child()
    }
  }
}

@ComponentV2
struct Child {
  @Consumer() str: string = 'world';

  build() {
    Column() {
      Button(this.str)
        .onClick(() => {
          this.str += '0';
        })
    }
  }
}

Establishing a Two-Way Binding Failed

In the following example, \@Provider and \@Consumer fail to establish a two-way synchronization relationship because of different aliasName value. 1. Initialize the Parent and Child custom components: - @Provider is not found when @Consumer() str: string = ‘world’ in the Child component searches upwards. - @Consumer() str: string = ‘world’ uses the local default value ‘world’. - Both of them fail to establish a two-way synchronization relationship. 2. Click the button in the Parent component to change @Provider decorated str1 and re-render only the Button component associated with @Provider. 3. Click the button in the Child component to change the @Consumer decorated str and re-render only the Button component associated with @Consumer.

@Entry
@ComponentV2
struct Parent {
  @Provider() str1: string = 'hello';

  build() {
    Column() {
      Button(this.str1)
        .onClick(() => {
          this.str1 += '0';
        })
      Child()
    }
  }
}

@ComponentV2
struct Child {
  @Consumer() str: string = 'world';

  build() {
    Column() {
      Button(this.str)
        .onClick(() => {
          this.str += '0';
        })
    }
  }
}

Decorating Callback by Using @Provider and @Consumer and Facilitating Behavior Abstraction Between Components

To register a callback function for a child component in a parent component, you can use \@Provider and \@Consumer to decorate a callback. For example, when a drag event occurs, if you want to synchronize the start position of the child component to the parent component, see the example below.

@Entry
@ComponentV2
struct Parent {
  @Local childX: number = 0;
  @Local childY: number = 1;
  @Provider() onDrag: (x: number, y: number) => void = (x: number, y: number) => {
    console.log(`onDrag event at x=${x} y:${y}`);
    this.childX = x;
    this.childY = y;
  }

  build() {
    Column() {
      Text(`child position x: ${this.childX}, y: ${this.childY}`)
      Child()
    }
  }
}

@ComponentV2
struct Child {
  @Consumer() onDrag: (x: number, y: number) => void = (x: number, y: number) => {};

  build() {
    Button("changed")
      .draggable(true)
      .onDragStart((event: DragEvent) => {
        // Current Previewer does not support common drag events.
        this.onDrag(event.getDisplayX(), event.getDisplayY());
      })
  }
}

Decorating Complex Types by \@Provider and \@Consumer and Using together with \@Trace

  1. \@Provider and \@Consumer can only observe the changes of the data. If they are used to decorate complex data types and you need to observe the changes of the properties, \@Trace is also required.
  2. When decorating built-in types, such as Array, Map, Set, and Date, you can observe the changes of some APIs. The observation capability is the same as that of \@Trace.
@ObservedV2
class User {
  @Trace name: string;
  @Trace age: number;

  constructor(name: string, age: number) {
    this.name = name;
    this.age = age;
  }
}
const data: User[] = [new User('Json', 10), new User('Eric', 15)];
@Entry
@ComponentV2
struct Parent {
  @Provider('data') users: User[] = data;

  build() {
    Column() {
      Child()
      Button('add new user')
        .onClick(() => {
          this.users.push(new User('Molly', 18));
        })
      Button('age++')
        .onClick(() => {
          this.users[0].age++;
        })
      Button('change name')
        .onClick(() => {
          this.users[0].name = 'Shelly';
        })
    }
  }
}

@ComponentV2
struct Child {
  @Consumer('data') users: User[] = [];

  build() {
    Column() {
      ForEach(this.users, (item: User) => {
        Column() {
          Text(`name: ${item.name}`).fontSize(30)
          Text(`age: ${item.age}`).fontSize(30)
          Divider()
        }
      })
    }
  }
}

Searching Upwards by \@Consumer for the Nearest \@Provider

If \@Provider has duplicate names in the component tree, \@Consumer will search upwards for the \@Provider data of the nearest parent node.

@Entry
@ComponentV2
struct Index {
  @Provider() val: number = 10;

  build() {
    Column() {
      Parent()
    }
  }
}

@ComponentV2
struct Parent {
  @Provider() val: number = 20;
  @Consumer("val") val2: number = 0; // 10

  build() {
    Column() {
      Text(`${this.val2}`)
      Child()
    }
  }
}

@ComponentV2
struct Child {
  @Consumer() val: number = 0; // 20

  build() {
    Column() {
      Text(`${this.val}`)
    }
  }
}

In the preceding example:

  • In Parent, \@Consumer searches upwards to find @Provider() val: number = 10 defined in Index. Therefore, the value is initialized to 10.
  • In Child, \@Consumer searches upwards to find @Provider() val: number = 20 defined in Parent and stops searching when it is found. Therefore, the value is initialized to 20.

Initializing \@Param by \@Provider and \@Consumer

  • Click Text(`Parent @Consumer val: ${this.val}`) to trigger the change of @Consumer() val. This change will be synchronized to @Provider() val in Index, triggering the re-render of the Text(Parent @Param val2: ${this.val2}) in the Child component.
  • The change of Parent @Consumer() val is also synchronized to Child, triggering the re-render of Text(Child @Param val ${this.val}).
@Entry
@ComponentV2
struct Index {
  @Provider() val: number = 10;

  build() {
    Column() {
      Parent({ val2: this.val })
    }
  }
}

@ComponentV2
struct Parent {
  @Consumer() val: number = 0;
  @Param val2: number = 0;

  build() {
    Column() {
      Text(`Parent @Consumer val: ${this.val}`).fontSize(30).onClick(() => {
        this.val++;
      })
      Text(`Parent @Param val2: ${this.val2}`).fontSize(30)
      Child({ val: this.val })
    }.border({ width: 2, color: Color.Green })
  }
}

@ComponentV2
struct Child {
  @Param val: number = 0;

  build() {
    Column() {
      Text(`Child @Param val ${this.val}`).fontSize(30)
    }.border({ width: 2, color: Color.Pink })
  }
}

你可能感兴趣的鸿蒙文章

harmony 鸿蒙\@AnimatableExtend Decorator: Definition of Animatable Attributes

harmony 鸿蒙Application State Management Overview

harmony 鸿蒙AppStorage: Storing Application-wide UI State

harmony 鸿蒙Basic Syntax Overview

harmony 鸿蒙\@Builder Decorator: Custom Builder Function

harmony 鸿蒙\@BuilderParam Decorator: Referencing the \@Builder Function

harmony 鸿蒙Creating a Custom Component

harmony 鸿蒙Mixing Use of Custom Components

harmony 鸿蒙Constraints on Access Modifiers of Custom Component Member Variables

harmony 鸿蒙Freezing a Custom Component

0  赞