• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# \@Watch Decorator: Getting Notified of State Variable Changes
2
3
4\@Watch is used to listen for state variables. If your application needs watch for value changes of a state variable, you can decorate the variable with \@Watch.
5
6
7> **NOTE**
8>
9> Since API version 9, this decorator is supported in ArkTS widgets.
10
11
12## Overview
13
14An application can request to be notified whenever the value of the \@Watch decorated variable changes. The \@Watch callback is called when the value change has occurred. \@Watch uses strict equality (===) to determine whether a value is updated in the ArkUI framework. If **false** is returned, the \@Watch callback is triggered.
15
16
17## Decorator Description
18
19| \@Watch Decorator| Description                                      |
20| -------------- | ---------------------------------------- |
21| Decorator parameters         | Mandatory. Constant string, which is quoted. Reference to a (string) => void custom component member function.|
22| Custom component variables that can be decorated   | All decorated state variables. Regular variables cannot be watched.              |
23| Order of decorators        | It is recommended that the \@State, \@Prop, \@Link, or other decorators precede the \@Watch decorator.|
24
25
26## Syntax
27
28| Type                                      | Description                                      |
29| ---------------------------------------- | ---------------------------------------- |
30| (changedPropertyName? : string) =&gt; void | This function is a member function of the custom component. **changedPropertyName** indicates the name of the watched attribute.<br>It is useful when you use the same function as a callback to several watched attributes.<br>It takes the attribute name as a string input parameter and returns nothing.|
31
32
33## Observed Changes and Behavior
34
351. When a state variable change (including the change of the named attribute in AppStorage or LocalStorage) is observed, the corresponding \@Watch callback is triggered.
36
372. \@The Watch callback is executed synchronously after the variable change in the custom component.
38
393. If the \@Watch callback mutates other watched variables, their variable @Watch callbacks in the same and other custom components as well as state updates are triggered.
40
414. A \@Watch function is not called upon custom component variable initialization, because initialization is not considered as variable mutation. A \@Watch function is called upon updating of the custom component variable.
42
43
44## Restrictions
45
46- Pay attention to the risk of infinite loops. Loops can be caused by the \@Watch callback directly or indirectly mutating the same variable. To avoid loops, avoid mutating the \@Watch decorated state variable inside the callback handler.
47
48- Pay attention to performance. The attribute value update function delays component re-render (see the preceding behavior description). The callback should only perform quick computations.
49
50- Calling **async await** from an \@Watch function is not recommended, because asynchronous behavior may cause performance issues of re-rendering.
51
52
53## Application Scenarios
54
55### \@Watch and Custom Component Update
56
57This example is used to clarify the processing steps of custom component updates and \@Watch. **count** is decorated by \@State in **CountModifier** and \@Prop in **TotalView**.
58
59
60```ts
61@Component
62struct TotalView {
63  @Prop @Watch('onCountUpdated') count: number = 0;
64  @State total: number = 0;
65  // @Watch callback
66  onCountUpdated(propName: string): void {
67    this.total += this.count;
68  }
69
70  build() {
71    Text(`Total: ${this.total}`)
72  }
73}
74
75@Entry
76@Component
77struct CountModifier {
78  @State count: number = 0;
79
80  build() {
81    Column() {
82      Button('add to basket')
83        .onClick(() => {
84          this.count++
85        })
86      TotalView({ count: this.count })
87    }
88  }
89}
90```
91
92Processing steps:
93
941. The click event **Button.onClick** of the **CountModifier** custom component increases the value of **count**.
95
962. In response to the change of the @State decorated variable **count**, \@Prop in the child component **TotalView** is updated, and its **\@Watch('onCountUpdated')** callback is triggered, which updates the **total** variable in **TotalView**.
97
983. The **Text** component in the child component **TotalView** is re-rendered.
99
100
101### Combination of \@Watch and \@Link
102
103This example illustrates how to watch an \@Link decorated variable in a child component.
104
105
106```ts
107class PurchaseItem {
108  static NextId: number = 0;
109  public id: number;
110  public price: number;
111
112  constructor(price: number) {
113    this.id = PurchaseItem.NextId++;
114    this.price = price;
115  }
116}
117
118@Component
119struct BasketViewer {
120  @Link @Watch('onBasketUpdated') shopBasket: PurchaseItem[];
121  @State totalPurchase: number = 0;
122
123  updateTotal(): number {
124    let total = this.shopBasket.reduce((sum, i) => sum + i.price, 0);
125    // A discount is provided when the amount exceeds 100 euros.
126    if (total >= 100) {
127      total = 0.9 * total;
128    }
129    return total;
130  }
131  // @Watch callback
132  onBasketUpdated(propName: string): void {
133    this.totalPurchase = this.updateTotal();
134  }
135
136  build() {
137    Column() {
138      ForEach(this.shopBasket,
139        (item:PurchaseItem) => {
140          Text(`Price: ${item.price.toFixed(2)} €`)
141        },
142        (item:PurchaseItem) => item.id.toString()
143      )
144      Text(`Total: ${this.totalPurchase.toFixed(2)} €`)
145    }
146  }
147}
148
149@Entry
150@Component
151struct BasketModifier {
152  @State shopBasket: PurchaseItem[] = [];
153
154  build() {
155    Column() {
156      Button('Add to basket')
157        .onClick(() => {
158          this.shopBasket.push(new PurchaseItem(Math.round(100 * Math.random())))
159        })
160      BasketViewer({ shopBasket: $shopBasket })
161    }
162  }
163}
164```
165
166The processing procedure is as follows:
167
1681. **Button.onClick** of the **BasketModifier** component adds an item to **BasketModifier shopBasket**.
169
1702. The value of the \@Link decorated variable **BasketViewer shopBasket** changes.
171
1723. The state management framework calls the \@Watch callback **BasketViewer onBasketUpdated** to update the value of **BasketViewer TotalPurchase**.
173
1744. Because \@Link decorated shopBasket changes (a new item is added), the ForEach component executes the item Builder to render and build the new item. Because the @State decorated totalPurchase variables changes, the **Text** component is also re-rendered. Re-rendering happens asynchronously.
175