1# Freezing a Custom Component 2 3When a custom component is inactive, it can be frozen so that its state variable does not respond to updates. That is, the @Watch decorated method is not called, and the node associated with the state variable is not re-rendered. You can use the **freezeWhenInactive** attribute to specify whether to freeze a custom component. If no parameter is passed in, the feature is disabled. This feature works in following scenarios: page routing, **\<TabContent>**, and **LazyforEach**. 4 5 6> **NOTE** 7> 8> Custom component freezing is supported since API version 11. 9 10## Use Scenarios 11 12### Page Routing 13 14- When page A calls the **router.pushUrl** API to jump to page B, page A is hidden and invisible. In this case, if the state variable on page A is updated, page A is not re-rendered. 15 16- The freezing feature does not work when the application is running in the background. 17 18Page A: 19 20```ts 21import router from '@ohos.router'; 22 23@Entry 24@Component({ freezeWhenInactive: true }) 25struct FirstTest { 26 @StorageLink('PropA') @Watch("first") storageLink: number = 47; 27 28 first() { 29 console.info("first page " + `${this.storageLink}`) 30 } 31 32 build() { 33 Column() { 34 Text(`From fist Page ${this.storageLink}`).fontSize(50) 35 Button('first page storageLink + 1').fontSize(30) 36 .onClick(() => { 37 this.storageLink += 1 38 }) 39 Button('go to next page').fontSize(30) 40 .onClick(() => { 41 router.pushUrl({ url: 'pages/second' }) 42 }) 43 } 44 } 45} 46``` 47 48Page B: 49 50```ts 51import router from '@ohos.router'; 52 53@Entry 54@Component({ freezeWhenInactive: true }) 55struct SecondTest { 56 @StorageLink('PropA') @Watch("second") storageLink2: number = 1; 57 58 second() { 59 console.info("second page: " + `${this.storageLink2}`) 60 } 61 62 build() { 63 Column() { 64 65 Text(`second Page ${this.storageLink2}`).fontSize(50) 66 Button('Change Divider.strokeWidth') 67 .onClick(() => { 68 router.back() 69 }) 70 71 Button('second page storageLink2 + 2').fontSize(30) 72 .onClick(() => { 73 this.storageLink2 += 2 74 }) 75 76 } 77 } 78} 79``` 80 81In the preceding example: 82 831. When the button **first page storLink + 1** on page A is clicked, the **storLink** state variable is updated, and the @Watch decorated **first** method is called. 84 852. Through **router.pushUrl({url:'pages/second'})**, page B is displayed, and page A is hidden with its state changing from active to inactive. 86 873. When the button **this.storLink2 += 2** on page B is clicked, only the @Watch decorated **second** method of page B is called, because page A has been frozen when inactive. 88 894. When the **back** button is clicked, page B is destroyed, and page A changes from inactive to active. At this time, if the state variable of page A is updated, the @Watch decorated **first** method of page A is called again. 90 91 92### \<TabContent> 93 94- You can freeze invisible **\<TabContent>** components in the **\<Tabs>** container so that they do not trigger UI re-rendering. 95 96- During initial rendering, only the **\<TabContent>** component that is being displayed is created. All **\<TabContent>** components are created only after all of them have been switched to. 97 98```ts 99@Entry 100@Component 101struct TabContentTest { 102 @State @Watch("onMessageUpdated") message: number = 0; 103 104 onMessageUpdated() { 105 console.info(`TabContent message callback func ${this.message}`) 106 } 107 108 build() { 109 Row() { 110 Column() { 111 Button('change message').onClick(() => { 112 this.message++ 113 }) 114 115 Tabs() { 116 TabContent() { 117 FreezeChild({ message: this.message }) 118 } 119 120 TabContent() { 121 FreezeChild({ message: this.message }) 122 } 123 } 124 } 125 .width('100%') 126 } 127 .height('100%') 128 } 129} 130 131@Component({ freezeWhenInactive: true }) 132struct FreezeChild { 133 @Link @Watch("onMessageUpdated") message: number 134 private index: number = 0 135 136 onMessageUpdated() { 137 console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`) 138 } 139 140 build() { 141 Text("message" + `${this.message}, index: ${this.index}`) 142 .fontSize(50) 143 .fontWeight(FontWeight.Bold) 144 } 145} 146``` 147 148In the preceding example: 149 1501. When **change message** is clicked, the value of **message** changes, and the @Watch decorated **onMessageUpdated** method of the **\<TabContent>** component being displayed is called. 151 1522. When you switch to another **\<TabContent>** component, it switches from inactive to active, and the corresponding @Watch decorated **onMessageUpdated** method is called. 153 1543. When **change message** is clicked again, the value of **message** changes, and only the @Watch decorated **onMessageUpdated** method of the **\<TabContent>** component being displayed is called. 155 156 157 158 159### LazyforEach 160 161- You can freeze custom components cached in **LazyforEach** so that they do not trigger UI re-rendering. 162 163```ts 164// Basic implementation of IDataSource to handle data listener 165class BasicDataSource implements IDataSource { 166 private listeners: DataChangeListener[] = []; 167 private originDataArray: string[] = []; 168 169 public totalCount(): number { 170 return 0; 171 } 172 173 public getData(index: number): string { 174 return this.originDataArray[index]; 175 } 176 177 // This method is called by the framework to add a listener to the LazyForEach data source. 178 registerDataChangeListener(listener: DataChangeListener): void { 179 if (this.listeners.indexOf(listener) < 0) { 180 console.info('add listener'); 181 this.listeners.push(listener); 182 } 183 } 184 185 // This method is called by the framework to remove the listener from the LazyForEach data source. 186 unregisterDataChangeListener(listener: DataChangeListener): void { 187 const pos = this.listeners.indexOf(listener); 188 if (pos >= 0) { 189 console.info('remove listener'); 190 this.listeners.splice(pos, 1); 191 } 192 } 193 194 // Notify LazyForEach that all child components need to be reloaded. 195 notifyDataReload(): void { 196 this.listeners.forEach(listener => { 197 listener.onDataReloaded(); 198 }) 199 } 200 201 // Notify LazyForEach that a child component needs to be added for the data item with the specified index. 202 notifyDataAdd(index: number): void { 203 this.listeners.forEach(listener => { 204 listener.onDataAdd(index); 205 }) 206 } 207 208 // Notify LazyForEach that the data item with the specified index has changed and the child component needs to be rebuilt. 209 notifyDataChange(index: number): void { 210 this.listeners.forEach(listener => { 211 listener.onDataChange(index); 212 }) 213 } 214 215 // Notify LazyForEach that the child component that matches the specified index needs to be deleted. 216 notifyDataDelete(index: number): void { 217 this.listeners.forEach(listener => { 218 listener.onDataDelete(index); 219 }) 220 } 221} 222 223class MyDataSource extends BasicDataSource { 224 private dataArray: string[] = []; 225 226 public totalCount(): number { 227 return this.dataArray.length; 228 } 229 230 public getData(index: number): string { 231 return this.dataArray[index]; 232 } 233 234 public addData(index: number, data: string): void { 235 this.dataArray.splice(index, 0, data); 236 this.notifyDataAdd(index); 237 } 238 239 public pushData(data: string): void { 240 this.dataArray.push(data); 241 this.notifyDataAdd(this.dataArray.length - 1); 242 } 243} 244 245@Entry 246@Component 247struct LforEachTest { 248 private data: MyDataSource = new MyDataSource(); 249 @State @Watch("onMessageUpdated") message: number = 0; 250 251 onMessageUpdated() { 252 console.info(`LazyforEach message callback func ${this.message}`) 253 } 254 255 aboutToAppear() { 256 for (let i = 0; i <= 20; i++) { 257 this.data.pushData(`Hello ${i}`) 258 } 259 } 260 261 build() { 262 Column() { 263 Button('change message').onClick(() => { 264 this.message++ 265 }) 266 List({ space: 3 }) { 267 LazyForEach(this.data, (item: string) => { 268 ListItem() { 269 FreezeChild({ message: this.message, 270 index: item }) 271 } 272 }, (item: string) => item) 273 }.cachedCount(5).height(500) 274 } 275 276 } 277} 278 279@Component({ freezeWhenInactive: true }) 280struct FreezeChild { 281 @Link @Watch("onMessageUpdated") message: number; 282 private index: string = ""; 283 284 aboutToAppear() { 285 console.info(`FreezeChild aboutToAppear index: ${this.index}`) 286 } 287 288 onMessageUpdated() { 289 console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`) 290 } 291 292 build() { 293 Text("message" + `${this.message}, index: ${this.index}`) 294 .width('90%') 295 .height(160) 296 .backgroundColor(0xAFEEEE) 297 .textAlign(TextAlign.Center) 298 .fontSize(30) 299 .fontWeight(FontWeight.Bold) 300 } 301} 302``` 303 304In the preceding example: 305 3061. When **change message** is clicked, the value of **message** changes, the @Watch decorated **onMessageUpdated** method of the list items being displayed is called, and that of the cached list items is not called. (If the component is not frozen, the @Watch decorated **onMessageUpdated** method of both list items that are being displayed and cached list items is called.) 307 3082. When a list item moves from outside the list content area into the list content area, it switches from inactive to active, and the corresponding @Watch decorated **onMessageUpdated** method is called. 309 3103. When **change message** is clicked again, the value of **message** changes, and only the @Watch decorated **onMessageUpdated** method of the list items being displayed is called. 311 312 313