1e41f4b71Sopenharmony_ci# Page and Custom Component Lifecycle
2e41f4b71Sopenharmony_ci
3e41f4b71Sopenharmony_ci
4e41f4b71Sopenharmony_ciBefore we dive into the page and custom component lifecycle, it would be helpful to learn the relationship between custom components and pages.
5e41f4b71Sopenharmony_ci
6e41f4b71Sopenharmony_ci
7e41f4b71Sopenharmony_ci- Custom component: \@Component decorated UI unit, which can combine multiple built-in components for component reusability and invoke component lifecycle callbacks.
8e41f4b71Sopenharmony_ci
9e41f4b71Sopenharmony_ci- Page: UI page of an application. A page can consist of one or more custom components. A custom component decorated with [@Entry](arkts-create-custom-components.md#basic-structure-of-a-custom-component) is used as the entry component of the page. Exactly one component is decorated with \@Entry in a single source file. Only components decorated by \@Entry can invoke the lifecycle callbacks of a page.
10e41f4b71Sopenharmony_ci
11e41f4b71Sopenharmony_ci
12e41f4b71Sopenharmony_ciThe following lifecycle callbacks are provided for a page, that is, a custom component decorated with \@Entry:
13e41f4b71Sopenharmony_ci
14e41f4b71Sopenharmony_ci
15e41f4b71Sopenharmony_ci- [onPageShow](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onpageshow): Invoked each time the page is displayed, for example, during page redirection or when the application is switched to the foreground.
16e41f4b71Sopenharmony_ci
17e41f4b71Sopenharmony_ci- [onPageHide](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onpagehide): Invoked each time the page is hidden, for example, during page redirection or when the application is switched to the background.
18e41f4b71Sopenharmony_ci
19e41f4b71Sopenharmony_ci- [onBackPress](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#onbackpress): Invoked when the user clicks the **Back** button.
20e41f4b71Sopenharmony_ci
21e41f4b71Sopenharmony_ci
22e41f4b71Sopenharmony_ciThe following lifecycle callbacks are provided for a custom component decorated with \@Component:
23e41f4b71Sopenharmony_ci
24e41f4b71Sopenharmony_ci
25e41f4b71Sopenharmony_ci- [aboutToAppear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttoappear): Invoked when the custom component is about to appear. Specifically, it is invoked after a new instance of the custom component is created and before its **build** function is executed.
26e41f4b71Sopenharmony_ci
27e41f4b71Sopenharmony_ci- [onDidBuild](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#ondidbuild12): Invoked after the **build()** function of the custom component is executed. Do not change state variables or use functions (such as **animateTo**) in **onDidBuild**. Otherwise, unstable UI performance may result.
28e41f4b71Sopenharmony_ci
29e41f4b71Sopenharmony_ci- [aboutToDisappear](../reference/apis-arkui/arkui-ts/ts-custom-component-lifecycle.md#abouttodisappear): Invoked when the custom component is about to be destroyed. Do not change state variables in the **aboutToDisappear** function as doing this can cause unexpected errors. For example, the modification of the **@Link** decorated variable may cause unstable application running.
30e41f4b71Sopenharmony_ci
31e41f4b71Sopenharmony_ci
32e41f4b71Sopenharmony_ciThe following figure shows the lifecycle of a component (page) decorated with \@Entry.
33e41f4b71Sopenharmony_ci
34e41f4b71Sopenharmony_ci
35e41f4b71Sopenharmony_ci![en-us_image_0000001502372786](figures/en-us_image_0000001502372786.png)
36e41f4b71Sopenharmony_ci
37e41f4b71Sopenharmony_ci
38e41f4b71Sopenharmony_ciBased on the preceding figure, let's look into the creation, re-rendering, and deletion of a custom component.
39e41f4b71Sopenharmony_ci
40e41f4b71Sopenharmony_ci
41e41f4b71Sopenharmony_ci## Custom Component Creation and Rendering
42e41f4b71Sopenharmony_ci
43e41f4b71Sopenharmony_ci1. Custom component creation: An instance of a custom component is created by the ArkUI framework.
44e41f4b71Sopenharmony_ci
45e41f4b71Sopenharmony_ci2. Initialization of custom component member variables: The member variables are initialized with locally defined defaults or component constructor parameters. The initialization happens in the document order, which is the order in which the member variables are defined.
46e41f4b71Sopenharmony_ci
47e41f4b71Sopenharmony_ci3. If defined, the component's **aboutToAppear** callback is invoked.
48e41f4b71Sopenharmony_ci
49e41f4b71Sopenharmony_ci4. On initial render, the **build** function of the built-in component is executed for rendering. If the child component is a custom component, the rendering creates an instance of the child component. During initial render, the framework records the mapping between state variables and components. When a state variable changes, the framework drives the related components to update.
50e41f4b71Sopenharmony_ci
51e41f4b71Sopenharmony_ci5. If defined, the component's **onDidBuild** callback is invoked.
52e41f4b71Sopenharmony_ci
53e41f4b71Sopenharmony_ci
54e41f4b71Sopenharmony_ci## Custom Component Re-rendering
55e41f4b71Sopenharmony_ci
56e41f4b71Sopenharmony_ciRe-rending of a custom component is triggered when its state variable is changed by an event handle (for example, when the click event is triggered) or by an update to the associated attribute in LocalStorage or AppStorage.
57e41f4b71Sopenharmony_ci
58e41f4b71Sopenharmony_ci
59e41f4b71Sopenharmony_ci1. The framework observes the state variable change and marks the component for re-rendering.
60e41f4b71Sopenharmony_ci
61e41f4b71Sopenharmony_ci2. Using the mapping tables – created in step 4 of the [custom component creation and rendering process](#custom-component-creation-and-rendering), the framework knows which UI components are managed by the state variable and which update functions are used for these UI components. With this knowledge, the framework executes only the update functions of these UI components.
62e41f4b71Sopenharmony_ci
63e41f4b71Sopenharmony_ci
64e41f4b71Sopenharmony_ci## Custom Component Deletion
65e41f4b71Sopenharmony_ci
66e41f4b71Sopenharmony_ciA custom component is deleted when the branch of the **if** statement or the number of arrays in **ForEach** changes.
67e41f4b71Sopenharmony_ci
68e41f4b71Sopenharmony_ci
69e41f4b71Sopenharmony_ci1. Before the component is deleted, the **aboutToDisappear** callback is invoked to mark the component for deletion. The component deletion mechanism of ArkUI is as follows: (1) The backend component is directly removed from the component tree and destroyed; (2) The reference to the destroyed component is released from the frontend components; (3) The JS Engine garbage collects the destroyed component.
70e41f4b71Sopenharmony_ci
71e41f4b71Sopenharmony_ci2. The custom component and all its variables are deleted. Any variables linked to this component, such as [@Link](arkts-link.md), [@Prop](arkts-prop.md), or [@StorageLink](arkts-appstorage.md#storagelink) decorated variables, are unregistered from their [synchronization sources](arkts-state-management-overview.md#basic-concepts).
72e41f4b71Sopenharmony_ci
73e41f4b71Sopenharmony_ci
74e41f4b71Sopenharmony_ciUse of **async await** is not recommended inside the **aboutToDisappear** callback. In case of an asynchronous operation (a promise or a callback) being started from the **aboutToDisappear** callback, the custom component will remain in the Promise closure until the function is executed, which prevents the component from being garbage collected.
75e41f4b71Sopenharmony_ci
76e41f4b71Sopenharmony_ci
77e41f4b71Sopenharmony_ciThe following example shows when the lifecycle callbacks are invoked:
78e41f4b71Sopenharmony_ci
79e41f4b71Sopenharmony_ci
80e41f4b71Sopenharmony_ci
81e41f4b71Sopenharmony_ci```ts
82e41f4b71Sopenharmony_ci// Index.ets
83e41f4b71Sopenharmony_ciimport { router } from '@kit.ArkUI';
84e41f4b71Sopenharmony_ci
85e41f4b71Sopenharmony_ci@Entry
86e41f4b71Sopenharmony_ci@Component
87e41f4b71Sopenharmony_cistruct MyComponent {
88e41f4b71Sopenharmony_ci  @State showChild: boolean = true;
89e41f4b71Sopenharmony_ci  @State btnColor:string = "#FF007DFF";
90e41f4b71Sopenharmony_ci
91e41f4b71Sopenharmony_ci  // Only components decorated by @Entry can call the lifecycle callbacks of a page.
92e41f4b71Sopenharmony_ci  onPageShow() {
93e41f4b71Sopenharmony_ci    console.info('Index onPageShow');
94e41f4b71Sopenharmony_ci  }
95e41f4b71Sopenharmony_ci  // Only components decorated by @Entry can call the lifecycle callbacks of a page.
96e41f4b71Sopenharmony_ci  onPageHide() {
97e41f4b71Sopenharmony_ci    console.info('Index onPageHide');
98e41f4b71Sopenharmony_ci  }
99e41f4b71Sopenharmony_ci
100e41f4b71Sopenharmony_ci  // Only components decorated by @Entry can call the lifecycle callbacks of a page.
101e41f4b71Sopenharmony_ci  onBackPress() {
102e41f4b71Sopenharmony_ci    console.info('Index onBackPress');
103e41f4b71Sopenharmony_ci    this.btnColor ="#FFEE0606";
104e41f4b71Sopenharmony_ci    return true // The value true means that the page executes its own return logic, and false (default) means that the default return logic is used.
105e41f4b71Sopenharmony_ci  }
106e41f4b71Sopenharmony_ci
107e41f4b71Sopenharmony_ci  // Component lifecycle
108e41f4b71Sopenharmony_ci  aboutToAppear() {
109e41f4b71Sopenharmony_ci    console.info('MyComponent aboutToAppear');
110e41f4b71Sopenharmony_ci  }
111e41f4b71Sopenharmony_ci
112e41f4b71Sopenharmony_ci  // Component lifecycle
113e41f4b71Sopenharmony_ci  onDidBuild() {
114e41f4b71Sopenharmony_ci    console.info('MyComponent onDidBuild');
115e41f4b71Sopenharmony_ci  }
116e41f4b71Sopenharmony_ci
117e41f4b71Sopenharmony_ci  // Component lifecycle
118e41f4b71Sopenharmony_ci  aboutToDisappear() {
119e41f4b71Sopenharmony_ci    console.info('MyComponent aboutToDisappear');
120e41f4b71Sopenharmony_ci  }
121e41f4b71Sopenharmony_ci
122e41f4b71Sopenharmony_ci  build() {
123e41f4b71Sopenharmony_ci    Column() {
124e41f4b71Sopenharmony_ci      // When this.showChild is true, create the Child child component and invoke Child aboutToAppear.
125e41f4b71Sopenharmony_ci      if (this.showChild) {
126e41f4b71Sopenharmony_ci        Child()
127e41f4b71Sopenharmony_ci      }
128e41f4b71Sopenharmony_ci      // When this.showChild is false, delete the Child child component and invoke Child aboutToDisappear.
129e41f4b71Sopenharmony_ci      Button('delete Child')
130e41f4b71Sopenharmony_ci      .margin(20)
131e41f4b71Sopenharmony_ci      .backgroundColor(this.btnColor)
132e41f4b71Sopenharmony_ci      .onClick(() => {
133e41f4b71Sopenharmony_ci        this.showChild = false;
134e41f4b71Sopenharmony_ci      })
135e41f4b71Sopenharmony_ci      // Push to the page and execute onPageHide.
136e41f4b71Sopenharmony_ci      Button('push to next page')
137e41f4b71Sopenharmony_ci        .onClick(() => {
138e41f4b71Sopenharmony_ci          router.pushUrl({ url: 'pages/page' });
139e41f4b71Sopenharmony_ci        })
140e41f4b71Sopenharmony_ci    }
141e41f4b71Sopenharmony_ci
142e41f4b71Sopenharmony_ci  }
143e41f4b71Sopenharmony_ci}
144e41f4b71Sopenharmony_ci
145e41f4b71Sopenharmony_ci@Component
146e41f4b71Sopenharmony_cistruct Child {
147e41f4b71Sopenharmony_ci  @State title: string = 'Hello World';
148e41f4b71Sopenharmony_ci  // Component lifecycle
149e41f4b71Sopenharmony_ci  aboutToDisappear() {
150e41f4b71Sopenharmony_ci    console.info('[lifeCycle] Child aboutToDisappear')
151e41f4b71Sopenharmony_ci  }
152e41f4b71Sopenharmony_ci
153e41f4b71Sopenharmony_ci  // Component lifecycle
154e41f4b71Sopenharmony_ci  onDidBuild() {
155e41f4b71Sopenharmony_ci    console.info('[lifeCycle] Child onDidBuild');
156e41f4b71Sopenharmony_ci  }
157e41f4b71Sopenharmony_ci
158e41f4b71Sopenharmony_ci  // Component lifecycle
159e41f4b71Sopenharmony_ci  aboutToAppear() {
160e41f4b71Sopenharmony_ci    console.info('[lifeCycle] Child aboutToAppear')
161e41f4b71Sopenharmony_ci  }
162e41f4b71Sopenharmony_ci
163e41f4b71Sopenharmony_ci  build() {
164e41f4b71Sopenharmony_ci    Text(this.title)
165e41f4b71Sopenharmony_ci      .fontSize(50)
166e41f4b71Sopenharmony_ci      .margin(20)
167e41f4b71Sopenharmony_ci      .onClick(() => {
168e41f4b71Sopenharmony_ci        this.title = 'Hello ArkUI';
169e41f4b71Sopenharmony_ci      })
170e41f4b71Sopenharmony_ci  }
171e41f4b71Sopenharmony_ci}
172e41f4b71Sopenharmony_ci```
173e41f4b71Sopenharmony_ci```ts
174e41f4b71Sopenharmony_ci// page.ets
175e41f4b71Sopenharmony_ci@Entry
176e41f4b71Sopenharmony_ci@Component
177e41f4b71Sopenharmony_cistruct page {
178e41f4b71Sopenharmony_ci  @State textColor: Color = Color.Black;
179e41f4b71Sopenharmony_ci  @State num: number = 0;
180e41f4b71Sopenharmony_ci
181e41f4b71Sopenharmony_ci  onPageShow() {
182e41f4b71Sopenharmony_ci    this.num = 5;
183e41f4b71Sopenharmony_ci  }
184e41f4b71Sopenharmony_ci
185e41f4b71Sopenharmony_ci  onPageHide() {
186e41f4b71Sopenharmony_ci    console.log("page onPageHide");
187e41f4b71Sopenharmony_ci  }
188e41f4b71Sopenharmony_ci
189e41f4b71Sopenharmony_ci  onBackPress() { // If the value is not set, false is used.
190e41f4b71Sopenharmony_ci    this.textColor = Color.Grey;
191e41f4b71Sopenharmony_ci    this.num = 0;
192e41f4b71Sopenharmony_ci  }
193e41f4b71Sopenharmony_ci
194e41f4b71Sopenharmony_ci  aboutToAppear() {
195e41f4b71Sopenharmony_ci    this.textColor = Color.Blue;
196e41f4b71Sopenharmony_ci  }
197e41f4b71Sopenharmony_ci
198e41f4b71Sopenharmony_ci  build() {
199e41f4b71Sopenharmony_ci    Column() {
200e41f4b71Sopenharmony_ci      Text (`num: ${this.num}`)
201e41f4b71Sopenharmony_ci        .fontSize(30)
202e41f4b71Sopenharmony_ci        .fontWeight(FontWeight.Bold)
203e41f4b71Sopenharmony_ci        .fontColor(this.textColor)
204e41f4b71Sopenharmony_ci        .margin(20)
205e41f4b71Sopenharmony_ci        .onClick(() => {
206e41f4b71Sopenharmony_ci          this.num += 5;
207e41f4b71Sopenharmony_ci        })
208e41f4b71Sopenharmony_ci    }
209e41f4b71Sopenharmony_ci    .width('100%')
210e41f4b71Sopenharmony_ci  }
211e41f4b71Sopenharmony_ci}
212e41f4b71Sopenharmony_ci```
213e41f4b71Sopenharmony_ci
214e41f4b71Sopenharmony_ciIn the preceding example, the **Index** page contains two custom components. One is **MyComponent** decorated with \@Entry, which is also the entry component (root node) of the page. The other is **Child**, which is a child component of **MyComponent**. Only components decorated by \@Entry can call the page lifecycle callbacks. Therefore, the lifecycle callbacks of the **Index** page – **onPageShow**, **onPageHide**, and **onBackPress**, are declared in **MyComponent**. In **MyComponent** and its child components, component lifecycle callbacks – **aboutToAppear**, **onDidBuild**, and **aboutToDisappear** – are also declared.
215e41f4b71Sopenharmony_ci
216e41f4b71Sopenharmony_ci
217e41f4b71Sopenharmony_ci- The initialization process of application cold start is as follows: MyComponent aboutToAppear -> MyComponent build -> MyComponent onDidBuild -> Child aboutToAppear -> Child build -> Child onDidBuild -> Index onPageShow
218e41f4b71Sopenharmony_ci
219e41f4b71Sopenharmony_ci- When **delete Child** is clicked, the value of **this.showChild** linked to **if** changes to **false**. As a result, the **Child** component is deleted, and the **Child aboutToDisappear** callback is invoked.
220e41f4b71Sopenharmony_ci
221e41f4b71Sopenharmony_ci
222e41f4b71Sopenharmony_ci- When **push to next page** is clicked, the **router.pushUrl** API is called to jump to the next page. As a result, the **Index** page is hidden, and the **Index onPageHide** callback is invoked. As the called API is **router.pushUrl**, which results in the Index page being hidden, but not destroyed, only the **onPageHide** callback is invoked. After a new page is displayed, the process of initializing the lifecycle of the new page is executed.
223e41f4b71Sopenharmony_ci
224e41f4b71Sopenharmony_ci- If **router.replaceUrl** is called, the **Index** page is destroyed. In this case, the execution of lifecycle callbacks changes to: Index onPageHide -> MyComponent aboutToDisappear -> Child aboutToDisappear. As aforementioned, a component is destroyed by directly removing it from the component tree. Therefore, **aboutToDisappear** of the parent component is called first, followed by **aboutToDisappear** of the child component, and then the process of initializing the lifecycle of the new page is executed.
225e41f4b71Sopenharmony_ci
226e41f4b71Sopenharmony_ci- When the **Back** button is clicked, the **Index onBackPress** callback is invoked, and the current **Index** page is destroyed.
227e41f4b71Sopenharmony_ci
228e41f4b71Sopenharmony_ci- When the application is minimized or switched to the background, the **Index onPageHide** callback is invoked. As the current **Index** page is not destroyed, **aboutToDisappear** of the component is not executed. When the application returns to the foreground, the **Index onPageShow** callback is invoked.
229e41f4b71Sopenharmony_ci
230e41f4b71Sopenharmony_ci
231e41f4b71Sopenharmony_ci- When the application exits, the following callbacks are executed in order: Index onPageHide -> MyComponent aboutToDisappear -> Child aboutToDisappear.
232e41f4b71Sopenharmony_ci
233e41f4b71Sopenharmony_ci## Custom Component's Listening for Page Changes
234e41f4b71Sopenharmony_ci
235e41f4b71Sopenharmony_ciYou can use the listener API in [Observer](../reference/apis-arkui/js-apis-arkui-observer.md#observeronrouterpageupdate11) to listen for page changes in custom components.
236e41f4b71Sopenharmony_ci
237e41f4b71Sopenharmony_ci```ts
238e41f4b71Sopenharmony_ci// Index.ets
239e41f4b71Sopenharmony_ciimport { uiObserver, router, UIObserver } from '@kit.ArkUI';
240e41f4b71Sopenharmony_ci
241e41f4b71Sopenharmony_ci@Entry
242e41f4b71Sopenharmony_ci@Component
243e41f4b71Sopenharmony_cistruct Index {
244e41f4b71Sopenharmony_ci  listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
245e41f4b71Sopenharmony_ci    let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
246e41f4b71Sopenharmony_ci    if (info.pageId == routerInfo?.pageId) {
247e41f4b71Sopenharmony_ci      if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
248e41f4b71Sopenharmony_ci        console.log(`Index onPageShow`);
249e41f4b71Sopenharmony_ci      } else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
250e41f4b71Sopenharmony_ci        console.log(`Index onPageHide`);
251e41f4b71Sopenharmony_ci      }
252e41f4b71Sopenharmony_ci    }
253e41f4b71Sopenharmony_ci  }
254e41f4b71Sopenharmony_ci  aboutToAppear(): void {
255e41f4b71Sopenharmony_ci    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
256e41f4b71Sopenharmony_ci    uiObserver.on('routerPageUpdate', this.listener);
257e41f4b71Sopenharmony_ci  }
258e41f4b71Sopenharmony_ci  aboutToDisappear(): void {
259e41f4b71Sopenharmony_ci    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
260e41f4b71Sopenharmony_ci    uiObserver.off('routerPageUpdate', this.listener);
261e41f4b71Sopenharmony_ci  }
262e41f4b71Sopenharmony_ci  build() {
263e41f4b71Sopenharmony_ci    Column() {
264e41f4b71Sopenharmony_ci      Text(`this page is ${this.queryRouterPageInfo()?.pageId}`)
265e41f4b71Sopenharmony_ci        .fontSize(25)
266e41f4b71Sopenharmony_ci      Button("push self")
267e41f4b71Sopenharmony_ci        .onClick(() => {
268e41f4b71Sopenharmony_ci          router.pushUrl({
269e41f4b71Sopenharmony_ci            url: 'pages/Index'
270e41f4b71Sopenharmony_ci          })
271e41f4b71Sopenharmony_ci        })
272e41f4b71Sopenharmony_ci      Column() {
273e41f4b71Sopenharmony_ci        SubComponent()
274e41f4b71Sopenharmony_ci      }
275e41f4b71Sopenharmony_ci    }
276e41f4b71Sopenharmony_ci  }
277e41f4b71Sopenharmony_ci}
278e41f4b71Sopenharmony_ci@Component
279e41f4b71Sopenharmony_cistruct SubComponent {
280e41f4b71Sopenharmony_ci  listener: (info: uiObserver.RouterPageInfo) => void = (info: uiObserver.RouterPageInfo) => {
281e41f4b71Sopenharmony_ci    let routerInfo: uiObserver.RouterPageInfo | undefined = this.queryRouterPageInfo();
282e41f4b71Sopenharmony_ci    if (info.pageId == routerInfo?.pageId) {
283e41f4b71Sopenharmony_ci      if (info.state == uiObserver.RouterPageState.ON_PAGE_SHOW) {
284e41f4b71Sopenharmony_ci        console.log(`SubComponent onPageShow`);
285e41f4b71Sopenharmony_ci      } else if (info.state == uiObserver.RouterPageState.ON_PAGE_HIDE) {
286e41f4b71Sopenharmony_ci        console.log(`SubComponent onPageHide`);
287e41f4b71Sopenharmony_ci      }
288e41f4b71Sopenharmony_ci    }
289e41f4b71Sopenharmony_ci  }
290e41f4b71Sopenharmony_ci  aboutToAppear(): void {
291e41f4b71Sopenharmony_ci    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
292e41f4b71Sopenharmony_ci    uiObserver.on('routerPageUpdate', this.listener);
293e41f4b71Sopenharmony_ci  }
294e41f4b71Sopenharmony_ci  aboutToDisappear(): void {
295e41f4b71Sopenharmony_ci    let uiObserver: UIObserver = this.getUIContext().getUIObserver();
296e41f4b71Sopenharmony_ci    uiObserver.off('routerPageUpdate', this.listener);
297e41f4b71Sopenharmony_ci  }
298e41f4b71Sopenharmony_ci  build() {
299e41f4b71Sopenharmony_ci    Column() {
300e41f4b71Sopenharmony_ci      Text(`SubComponent`)
301e41f4b71Sopenharmony_ci    }
302e41f4b71Sopenharmony_ci  }
303e41f4b71Sopenharmony_ci}
304e41f4b71Sopenharmony_ci```
305