• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Creating a Custom Component
2
3
4In ArkUI, components are what's displayed on the UI. They can be classified as built-in components – those directly provided by ArkUI framework, and custom components – those defined by developers. Defining the entire application UI with just built-in components would lead to a monolithic design, low code maintainability, and poor execution performance. A good UI is the result of a well-thought-out development process, with such factors as code reusability, separation of service logic from the UI, and version evolution carefully considered. Creating custom components that encapsulate the UI and some business logic is a critical step in this process.
5
6
7The custom component has the following features:
8
9
10- Combinable: allows you to combine built-in components and other components, as well as their attributes and methods.
11
12- Reusable: can be reused by other components and used as different instances in different parent components or containers.
13
14- Data-driven update: holds some state and triggers UI re-rendering with the change of state variables.
15
16
17The following example shows the basic usage of a custom component.
18
19
20
21```ts
22@Component
23struct HelloComponent {
24  @State message: string = 'Hello, World!';
25
26  build() {
27    // The HelloComponent custom component combines the <Row> and <Text> built-in components.
28    Row() {
29      Text(this.message)
30        .onClick(() => {
31          // The change of the state variable message drives the UI to be re-rendered. As a result, the text changes from "Hello, World!" to "Hello, ArkUI!".
32          this.message = 'Hello, ArkUI!';
33        })
34    }
35  }
36}
37```
38
39
40Multiple **HelloComponent** instances can be created in the **build()** function of other custom components. In this way, **HelloComponent** is reused by those custom components.
41
42
43
44```ts
45@Entry
46@Component
47struct ParentComponent {
48  build() {
49    Column() {
50      Text('ArkUI message')
51      HelloComponent({ message: 'Hello, World!' });
52      Divider()
53      HelloComponent ({ message: 'Hello!' });
54    }
55  }
56}
57```
58
59
60To fully understand the preceding example, a knowledge of the following concepts is essential:
61
62
63- [Basic Structure of a Custom Component](#basic-structure-of-a-custom-component)
64
65- [Member Functions/Variables](#member-functionsvariables)
66
67- [Rules of for Custom Component Parameters](#rules-of-for-custom-component-parameters)
68
69- [build Function](#build-function)
70
71- [Universal Style of a Custom Component](#universal-style-of-a-custom-component)
72
73- [Custom Attribute Methods](#custom-attribute-methods)
74
75
76## Basic Structure of a Custom Component
77
78- struct: The definition of a custom component must start with the \@Component struct followed by the component name, and then component body enclosed by curly brackets {....}. No inheritance is allowed. You can omit the **new** operator when instantiating a struct.
79  > **NOTE**
80  >
81  > The name or its class or function name of a custom component must be different from that of any built-in components.
82
83- \@Component: The \@Component decorator can decorate only the data structures declared by the **struct** keyword. After being decorated by \@Component, a struct has the componentization capability. It must implement the **build** function to describe the UI. One struct can be decorated by only one \@Component.
84  > **NOTE**
85  >
86  > Since API version 9, this decorator is supported in ArkTS widgets.
87
88  ```ts
89  @Component
90  struct MyComponent {
91  }
92  ```
93
94- build(): The **build()** function is used to define the declarative UI description of a custom component. Every custom component must define a **build()** function.
95
96  ```ts
97  @Component
98  struct MyComponent {
99    build() {
100    }
101  }
102  ```
103
104- \@Entry: A custom component decorated with \@Entry is used as the default entry component of the page. At most one component can be decorated with \@Entry in a single source file. The \@Entry decorator accepts an optional parameter of type [LocalStorage](arkts-localstorage.md).
105
106  > **NOTE**
107  >
108  > Since API version 9, this decorator is supported in ArkTS widgets.
109
110  ```ts
111  @Entry
112  @Component
113  struct MyComponent {
114  }
115  ```
116
117
118## Member Functions/Variables
119
120In addition to the mandatory **build()** function, a custom component may implement other member functions with the following restrictions:
121
122
123- Static functions are not supported.
124
125- Access to the member functions is always private.
126
127
128A custom component can also implement member variables with the following restrictions:
129
130
131- Static member variables are not supported.
132
133- Access to the member variables is always private. The access rules of member variables are the same as those of member functions.
134
135- Local initialization is optional for some member variables and mandatory for others. For details about whether local initialization or initialization from the parent component is required, see [State Management](arkts-state-management-overview.md).
136
137
138## Rules of for Custom Component Parameters
139
140As can be learnt from preceding examples, a custom component can be created from a **build** or [@Builder](arkts-builder.md) function, and during the creation, parameters can be supplied to the component.
141
142
143```ts
144@Component
145struct MyComponent {
146  private countDownFrom: number = 0;
147  private color: Color = Color.Blue;
148
149  build() {
150  }
151}
152
153@Entry
154@Component
155struct ParentComponent {
156  private someColor: Color = Color.Pink;
157
158  build() {
159    Column() {
160      // Create an instance of MyComponent and initialize its countDownFrom variable with the value 10 and its color variable with the value this.someColor.
161      MyComponent({ countDownFrom: 10, color: this.someColor })
162    }
163  }
164}
165```
166
167
168## build Function
169
170All languages declared in the **build** function are called UI description languages. The UI description languages must comply with the following rules:
171
172- For an \@Entry decorated custom component, exactly one root component is required under the **build** function. This root component must be a container component. **ForEach** is not allowed at the top level.
173  For an \@Component decorated custom component, exactly one root component is required under the **build** function. This root component is not necessarily a container component. **ForEach** is not allowed at the top level.
174
175  ```ts
176  @Entry
177  @Component
178  struct MyComponent {
179    build() {
180      // Exactly one root component is required, and it must be a container component.
181      Row() {
182        ChildComponent()
183      }
184    }
185  }
186
187  @Component
188  struct ChildComponent {
189    build() {
190      // Exactly one root component is required, and it is not necessarily a container component.
191      Image('test.jpg')
192    }
193  }
194  ```
195
196- Local variable declaration is not allowed. The following example is invalid:
197
198  ```ts
199  build() {
200    // Invalid: Local variable declaration is not allowed.
201    let a: number = 1;
202  }
203  ```
204
205- **console.info** cannot be directly used in the UI description, but can be used in methods or functions. The following is an example:
206
207  ```ts
208  build() {
209    // Invalid: Use of console.info is not allowed.
210    console.info('print debug log');
211  }
212  ```
213
214- Creation of a local scope is not allowed. The following example is invalid:
215
216  ```ts
217  build() {
218    // Invalid: Creation of local scope is not allowed.
219    {
220      ...
221    }
222  }
223  ```
224
225- Calling a function other than the \@Builder decorated is not allowed. The parameters of built-in components can be the return values of TS methods.
226
227  ```ts
228  @Component
229  struct ParentComponent {
230    doSomeCalculations() {
231    }
232
233    calcTextValue(): string {
234      return 'Hello World';
235    }
236
237    @Builder doSomeRender() {
238      Text(`Hello World`)
239    }
240
241    build() {
242      Column() {
243        // Invalid: No function calls except @Builder functions.
244        this.doSomeCalculations();
245        // Valid: The function can be called.
246        this.doSomeRender();
247        // Valid: The parameter can be the return value of a TS method.
248        Text(this.calcTextValue())
249      }
250    }
251  }
252  ```
253
254- The **switch** syntax is not allowed. Use **if** instead. The following example is invalid:
255
256  ```ts
257  build() {
258    Column() {
259      // Invalid: The switch syntax is not allowed.
260      switch (expression) {
261        case 1:
262          Text('...')
263          break;
264        case 2:
265          Image('...')
266          break;
267        default:
268          Text('...')
269          break;
270      }
271    }
272  }
273  ```
274
275- Expressions are not allowed. The following example is invalid:
276
277  ```ts
278  build() {
279    Column() {
280      // Invalid: Expressions are not allowed.
281      (this.aVar > 10) ? Text('...') : Image('...')
282    }
283  }
284  ```
285
286
287## Universal Style of a Custom Component
288
289The universal style of a custom component is configured by invoking chainable attribute methods.
290
291
292```ts
293@Component
294struct MyComponent2 {
295  build() {
296    Button(`Hello World`)
297  }
298}
299
300@Entry
301@Component
302struct MyComponent {
303  build() {
304    Row() {
305      MyComponent2()
306        .width(200)
307        .height(300)
308        .backgroundColor(Color.Red)
309    }
310  }
311}
312```
313
314> **NOTE**
315>
316> When ArkUI sets styles for custom components, an invisible container component is set for **MyComponent2**. These styles are set on the container component instead of the **\<Button>** component of **MyComponent2**. As seen from the rendering result, the red background color is not directly applied to the button. Instead, it is applied to the container component that is invisible to users where the button is located.
317
318
319## Custom Attribute Methods
320
321Custom components do not support custom attribute methods. You can use the Controller capability to implement custom APIs.
322
323
324```ts
325// Custom controller
326export class MyComponentController {
327  item: MyComponent = null;
328
329  setItem(item: MyComponent) {
330    this.item = item;
331  }
332
333  changeText(value: string) {
334    this.item.value = value;
335  }
336}
337
338// Custom component
339@Component
340export default struct MyComponent {
341  public controller: MyComponentController = null;
342  @State value: string = 'Hello World';
343
344  build() {
345    Column() {
346      Text(this.value)
347        .fontSize(50)
348    }
349  }
350
351  aboutToAppear() {
352    if (this.controller)
353      this.controller.setItem (this); // Link to the controller.
354  }
355}
356
357// Processing logic
358@Entry
359@Component
360struct StyleExample {
361  controller = new MyComponentController();
362
363  build() {
364    Column() {
365      MyComponent({ controller: this.controller })
366    }
367    .onClick(() => {
368      this.controller.changeText('Text');
369    })
370  }
371}
372```
373
374In the preceding example:
375
3761. The **aboutToAppear** method of the **MyComponent** child component passes the current **this** pointer to the **item** member variable of **MyComponentController**.
377
3782. The **StyleExample** parent component holds a **Controller** instance and with which calls the **changeText** API of **Controller**. That is, the value of the state variable **value** of **MyComponent** is changed through the **this** pointer of the **MyComponent** child component held by the controller.
379
380Through the encapsulation of the controller, **MyComponent** exposes the **changeText** API. All instances that hold the controller can call the **changeText** API to change the value of the **MyComponent** state variable **value**.
381