1# Freezing a Custom Component 2 3When a custom component decorated by @ComponentV2 is inactive, it can be frozen so that its state variable does not respond to updates. That is, the @Monitor 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 **Navigation**. 4 5 6> **NOTE** 7> 8> Custom component freezing is supported since API version 12. 9> 10> Different from freezing the @Component decorated components, custom components decorated by @ComponentV2 do not support freezing the cached list items in the **LazyForEach** scenario. 11 12 13## Use Scenarios 14 15### Page Routing 16 17- 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. 18 19- The freezing feature does not work when the application is running in the background. 20 21Page A: 22 23```ts 24import { router } from '@kit.ArkUI'; 25 26@ObservedV2 27export class Book { 28 @Trace page: number = 100; 29 30 constructor(page: number) { 31 this.page = page; 32 } 33 34 @Monitor("page") 35 onPageChange(monitor: IMonitor) { 36 console.log(`Page change : ${this.page}`); 37 } 38} 39 40export let book: Book = new Book(100); 41 42@Entry 43@ComponentV2({ freezeWhenInactive: true }) 44export struct FirstTest { 45 build() { 46 Column() { 47 Text(`From fist Page ${book.page}`).fontSize(50) 48 Button('first page + 1').fontSize(30) 49 .onClick(() => { 50 book.page += 1; 51 }) 52 Button('go to next page').fontSize(30) 53 .onClick(() => { 54 router.pushUrl({ url: 'pages/Page' }); 55 }) 56 } 57 } 58} 59``` 60 61Page B: 62 63```ts 64import { router } from '@kit.ArkUI'; 65import { book } from './Index'; 66 67@Entry 68@ComponentV2({ freezeWhenInactive: true }) 69struct SecondTest { 70 build() { 71 Column() { 72 Text(`second Page ${book.page}`).fontSize(50) 73 Button('Back') 74 .onClick(() => { 75 router.back(); 76 }) 77 Button('second page + 2').fontSize(30) 78 .onClick(() => { 79 book.page += 2; 80 }) 81 82 } 83 } 84} 85``` 86 87In the preceding example: 88 891. When the button **first page + 1** on page A is clicked, the **page** property of the **book** variable is updated, and the @Monitor decorated **onPageChange** method is called. 90 912. Through **router.pushUrl({url: 'pages/Page'})**, page B is displayed, and page A is hidden with its state changing from active to inactive. 92 933. When the button **second page + 2** on page B is clicked, the @Monitor decorated **onPageChange** method is called. 94 954. When the **back** button is clicked, page B is destroyed, and page A changes from inactive to active. At this time, the **Text** component is updated. 96 97 98### TabContent 99 100- You can freeze invisible **TabContent** components in the **Tabs** container so that they do not trigger UI re-rendering. 101 102- 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. 103 104```ts 105@Entry 106@ComponentV2 107struct TabContentTest { 108 @Local message: number = 0; 109 @Local data: number[] = [0, 1]; 110 111 build() { 112 Row() { 113 Column() { 114 Button('change message').onClick(() => { 115 this.message++; 116 }) 117 118 Tabs() { 119 ForEach(this.data, (item: number) => { 120 TabContent() { 121 FreezeChild({ message: this.message, index: item }) 122 }.tabBar(`tab${item}`) 123 }, (item: number) => item.toString()) 124 } 125 } 126 .width('100%') 127 } 128 .height('100%') 129 } 130} 131 132@ComponentV2({ freezeWhenInactive: true }) 133struct FreezeChild { 134 @Param message: number = 0; 135 @Param index: number = 0; 136 137 @Monitor('message') onMessageUpdated(mon: IMonitor) { 138 console.info(`FreezeChild message callback func ${this.message}, index: ${this.index}`); 139 } 140 141 build() { 142 Text("message" + `${this.message}, index: ${this.index}`) 143 .fontSize(50) 144 .fontWeight(FontWeight.Bold) 145 } 146} 147``` 148 149In the preceding example: 150 1511. When **change message** is clicked, the value of **message** changes, and the @Monitor decorated **onMessageUpdated** method of the **TabContent** component being displayed is called. 152 1532. When **two** is clicked to switch to another **TabContent** component, the component switches from inactive to active, and the corresponding @Monitor decorated **onMessageUpdated** method is called. 154 1553. When **change message** is clicked again, the value of **message** changes, and only the @Monitor decorated **onMessageUpdated** method of the **TabContent** component being displayed is called. 156 157 158### Navigation 159 160- You can freeze an invisible page so that it does not trigger UI re-rendering. When the user returns to this page, a re-render is triggered through an @Monitor decorated callback. 161 162```ts 163@Entry 164@ComponentV2 165struct MyNavigationTestStack { 166 @Provider('pageInfo') pageInfo: NavPathStack = new NavPathStack(); 167 @Local message: number = 0; 168 169 @Monitor('message') info() { 170 console.info(`freeze-test MyNavigation message callback ${this.message}`); 171 } 172 173 @Builder 174 PageMap(name: string) { 175 if (name === 'pageOne') { 176 pageOneStack({ message: this.message }) 177 } else if (name === 'pageTwo') { 178 pageTwoStack({ message: this.message }) 179 } else if (name === 'pageThree') { 180 pageThreeStack({ message: this.message }) 181 } 182 } 183 184 build() { 185 Column() { 186 Button('change message') 187 .onClick(() => { 188 this.message++; 189 }) 190 Navigation(this.pageInfo) { 191 Column() { 192 Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) 193 .onClick(() => { 194 this.pageInfo.pushPath({ name: 'pageOne' }); // Push the navigation destination page specified by name to the navigation stack. 195 }) 196 } 197 }.title('NavIndex') 198 .navDestination(this.PageMap) 199 .mode(NavigationMode.Stack) 200 } 201 } 202} 203 204@ComponentV2 205struct pageOneStack { 206 @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); 207 @Local index: number = 1; 208 @Param message: number = 0; 209 210 build() { 211 NavDestination() { 212 Column() { 213 NavigationContentMsgStack({ message: this.message, index: this.index }) 214 Text("cur stack size:" + `${this.pageInfo.size()}`) 215 .fontSize(30) 216 Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) 217 .onClick(() => { 218 this.pageInfo.pushPathByName('pageTwo', null); 219 }) 220 Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) 221 .onClick(() => { 222 this.pageInfo.pop(); 223 }) 224 }.width('100%').height('100%') 225 }.title('pageOne') 226 .onBackPressed(() => { 227 this.pageInfo.pop(); 228 return true; 229 }) 230 } 231} 232 233@ComponentV2 234struct pageTwoStack { 235 @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); 236 @Local index: number = 2; 237 @Param message: number = 0; 238 239 build() { 240 NavDestination() { 241 Column() { 242 NavigationContentMsgStack({ message: this.message, index: this.index }) 243 Text("cur stack size:" + `${this.pageInfo.size()}`) 244 .fontSize(30) 245 Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) 246 .onClick(() => { 247 this.pageInfo.pushPathByName('pageThree', null); 248 }) 249 Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) 250 .onClick(() => { 251 this.pageInfo.pop(); 252 }) 253 } 254 }.title('pageTwo') 255 .onBackPressed(() => { 256 this.pageInfo.pop(); 257 return true; 258 }) 259 } 260} 261 262@ComponentV2 263struct pageThreeStack { 264 @Consumer('pageInfo') pageInfo: NavPathStack = new NavPathStack(); 265 @Local index: number = 3; 266 @Param message: number = 0; 267 268 build() { 269 NavDestination() { 270 Column() { 271 NavigationContentMsgStack({ message: this.message, index: this.index }) 272 Text("cur stack size:" + `${this.pageInfo.size()}`) 273 .fontSize(30) 274 Button('Next Page', { stateEffect: true, type: ButtonType.Capsule }) 275 .height(40) 276 .onClick(() => { 277 this.pageInfo.pushPathByName('pageOne', null); 278 }) 279 Button('Back Page', { stateEffect: true, type: ButtonType.Capsule }) 280 .height(40) 281 .onClick(() => { 282 this.pageInfo.pop(); 283 }) 284 } 285 }.title('pageThree') 286 .onBackPressed(() => { 287 this.pageInfo.pop(); 288 return true; 289 }) 290 } 291} 292 293@ComponentV2({ freezeWhenInactive: true }) 294struct NavigationContentMsgStack { 295 @Param message: number = 0; 296 @Param index: number = 0; 297 298 @Monitor('message') info() { 299 console.info(`freeze-test NavigationContent message callback ${this.message}`); 300 console.info(`freeze-test ---- called by content ${this.index}`); 301 } 302 303 build() { 304 Column() { 305 Text("msg:" + `${this.message}`) 306 .fontSize(30) 307 } 308 } 309} 310``` 311 312In the preceding example: 313 3141. When **change message** is clicked, the value of **message** changes, and the @Monitor decorated **info** method of the **MyNavigationTestStack** component being displayed is called. 315 3162. When **Next Page** is clicked, the page is switched to **PageOne** and the **pageOneStack** node is created. 317 3183. When **change message** is clicked again, the value of **message** changes, and only the @Monitor decorated **info** method of the **NavigationContentMsgStack** child component in **pageOneStack** is called. 319 3204. When **Next Page** is clicked, the page is switched to **PageTwo** and the **pageTwoStack** node is created. 321 3225. When **change message** is clicked again, the value of **message** changes, and only the @Monitor decorated **info** method of the **NavigationContentMsgStack** child component in **pageTwoStack** is called. 323 3246. When **Next Page** is clicked, the page is switched to **PageThree** and the **pageThreeStack** node is created. 325 3267. When **change message** is clicked again, the value of **message** changes, and only the @Monitor decorated **info** method of the **NavigationContentMsgStack** child component in **pageThreeStack** is called. 327 3288. When **Back Page** is clicked, the page is switched to **PageTwo**. At this time, only the @Monitor decorated **info** method of the **NavigationContentMsgStack** child component in **pageTwoStack** is called. 329 3309. When **Back Page** is clicked, the page is switched to **PageOne** page. At this time, only the @Monitor decorated **info** method of the **NavigationContentMsgStack** child component in **pageOneStack** is called. 331 33210. When **Back Page** is clicked, the page is switched to the initial page. 333