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