• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Best Practices for Rendering Control
2
3
4This guide outlines best practices for rendering control in ArkUI applications. Read on to discover the common pitfalls in state management and how to avoid them, with carefully selected examples of recommended and not-recommended practices.
5
6
7## Adding Elements to the ForEach Data Source
8
9Adding elements to the ForEach data source causes duplicate array item IDs.
10
11
12### Not Recommended
13
14  The following example uses ForEach to iterate through each element of the **this.arr** array, displays the elements in the **\<Text>** component, and adds a new array element when **Text('Add arr element')** is clicked.
15
16```ts
17@Entry
18@Component
19struct Index {
20  @State arr: number[] = [1,2,3];
21
22  build() {
23      Column() {
24       ForEach(this.arr,
25         (item: void) => {
26           Text(`Item ${item}`)
27         },
28         (item: string) => item.toString())
29       Text('Add arr element')
30         .fontSize(20)
31         .onClick(()=>{
32           this.arr.push(4); // New element in the array, whose key is '4' in ForEach.
33           console.log("Arr elements: ", this.arr);
34         })
35      }
36    }
37}
38```
39
40When **Text('Add arr element')** is clicked twice, a new element with the key of **4** is added to the **this.arr** array each time. The third parameter **(item)=&gt; item.toString()** in ForEach is a key generator function. It needs to generate an ID for each item in the array. This ID must be unique and stable. Specifically:
41
42- The ID of each array item generated by the key generator function must be unique.
43
44- When an array item ID changes, the ArkUI framework regards the array item as having been replaced or changed.
45
46- The ArkUI framework generates alarms for duplicate IDs. In this case, the framework behavior is unknown, and the UI update in particular may not work.
47
48Therefore, in the preceding example, the framework does not display the text element added at the second and later attempts. This is because the element is not unique, with the same key (4) as the other elements. If **(item) =&gt; item.toString()** of ForEach is deleted, the framework will use the default array ID generator function, that is, **(item: any, index: number) =&gt; '${index}__${JSON.stringify(item)}'**. In this case, each newly added text element is updated after the **onClick** event is triggered. Though the default array ID generator function has better compatibility, it may cause unnecessary UI updates. Therefore, a custom key generator function is recommended.
49
50
51## Updating the ForEach Data Source
52
53When the ForEach data source is updated, the array item whose ID is the same as the original array item ID is not re-created.
54
55
56### Not Recommended
57
58The following example defines the **Index** and **Child** components. The parent component **Index** has member variables of the **arr** array. The initial values include numbers 1, 2, and 3. In the **Child** component, the \@Prop decorated variable **value** is defined to receive an element in the **arr** array in the parent component.
59
60
61```ts
62@Component
63struct Child {
64  @Prop value: number = 0;
65  build() {
66    Text(`${this.value}`)
67      .fontSize(50)
68      .onClick(() => {
69        this.value++ // Click to change the value of @Prop.
70      })
71  }
72}
73@Entry
74@Component
75struct Index {
76  @State arr: number[] = [1, 2, 3];
77  build() {
78    Row() {
79      Column() {
80        // Control group
81        Child({ value: this.arr[0] })
82        Child({ value: this.arr[1] })
83        Child({ value: this.arr[2] })
84        Divider().height(5)
85        ForEach(this.arr,
86          (item: number) => {
87            Child({ value: item })
88          },
89          (item: string) => item.toString() // Key value and ID
90        )
91        Text('Parent: replace entire arr')
92          .fontSize(50)
93          .onClick(() => {
94            // Both array items contain '3', and the ID in ForEach does not change.
95            // This means that ForEach does not update the Child instance, and @Prop is not updated in the parent component.
96            this.arr = (this.arr[0] == 1) ? [3, 4, 5] : [1, 2, 3];
97          })
98      }
99    }
100  }
101}
102```
103
104When the **onClick** event of the **\<Text>** component **Parent: replace entire arr** is triggered, the state variable array **arr** is replaced with [3, 4, 5] or [1, 2, 3] based on the value of the first element. The **Child** component initially created in ForEach, whose \@Prop decorated variable's received value is 3, however, is not updated.
105
106This is because both the original and new arrays contain an element with the same value (number 3), and the ID generated for the element does not change in the parent component. As a result, ForEach does not identify the need for value changes in the **Child** component, and \@Prop in the component is not updated.
107
108![en-us_image_0000001604900446](figures/en-us_image_0000001604900446.png)
109
110You can replace the duplicate element with a unique element in **arr** and observe the behavior, which should be as expected.
111