UNPKG

6.73 kBTypeScriptView Raw
1declare module '@glimmer/tracking' {
2 export { tracked, cached } from '@ember/-internals/metal';
3 /**
4 In order to tell Ember a value might change, we need to mark it as trackable.
5 Trackable values are values that:
6
7 - Can change over their component’s lifetime and
8 - Should cause Ember to rerender if and when they change
9
10 We can do this by marking the field with the `@tracked` decorator.
11
12 @module @glimmer/tracking
13 @public
14 */
15 /**
16 Marks a property as tracked. By default, values that are rendered in Ember app
17 templates are _static_, meaning that updates to them won't cause the
18 application to rerender. Marking a property as tracked means that when that
19 property changes, any templates that used that property, directly or
20 indirectly, will rerender. For instance, consider this component:
21
22 ```handlebars
23 <div>Count: {{this.count}}</div>
24 <div>Times Ten: {{this.timesTen}}</div>
25 <div>
26 <button {{on "click" this.plusOne}}>
27 Plus One
28 </button>
29 </div>
30 ```
31
32 ```javascript
33 import Component from '@glimmer/component';
34 import { tracked } from '@glimmer/tracking';
35 import { action } from '@ember/object';
36
37 export default class CounterComponent extends Component {
38 @tracked count = 0;
39
40 get timesTen() {
41 return this.count * 10;
42 }
43
44 @action
45 plusOne() {
46 this.count += 1;
47 }
48 }
49 ```
50
51 Both the `{{this.count}}` and the `{{this.timesTen}}` properties in the
52 template will update whenever the button is clicked. Any tracked properties
53 that are used in any way to calculate a value that is used in the template
54 will cause a rerender when updated - this includes through method calls and
55 other means:
56
57 ```javascript
58 import Component from '@glimmer/component';
59 import { tracked } from '@glimmer/tracking';
60
61 class Entry {
62 @tracked name;
63 @tracked phoneNumber;
64
65 constructor(name, phoneNumber) {
66 this.name = name;
67 this.phoneNumber = phoneNumber;
68 }
69 }
70
71 export default class PhoneBookComponent extends Component {
72 entries = [
73 new Entry('Pizza Palace', 5551234),
74 new Entry('1st Street Cleaners', 5554321),
75 new Entry('Plants R Us', 5552468),
76 ];
77
78 // Any usage of this property will update whenever any of the names in the
79 // entries arrays are updated
80 get names() {
81 return this.entries.map(e => e.name);
82 }
83
84 // Any usage of this property will update whenever any of the numbers in the
85 // entries arrays are updated
86 get numbers() {
87 return this.getFormattedNumbers();
88 }
89
90 getFormattedNumbers() {
91 return this.entries
92 .map(e => e.phoneNumber)
93 .map(number => {
94 let numberString = '' + number;
95
96 return numberString.slice(0, 3) + '-' + numberString.slice(3);
97 });
98 }
99 }
100 ```
101
102 It's important to note that setting tracked properties will always trigger an
103 update, even if the property is set to the same value as it was before.
104
105 ```js
106 let entry = new Entry('Pizza Palace', 5551234);
107 // if entry was used when rendering, this would cause a rerender, even though
108 // the name is being set to the same value as it was before
109 entry.name = entry.name;
110 ```
111
112 `tracked` can also be used with the classic Ember object model in a similar
113 manner to classic computed properties:
114
115 ```javascript
116 import EmberObject from '@ember/object';
117 import { tracked } from '@glimmer/tracking';
118
119 const Entry = EmberObject.extend({
120 name: tracked(),
121 phoneNumber: tracked()
122 });
123 ```
124
125 Often this is unnecessary, but to ensure robust auto-tracking behavior it is
126 advisable to mark tracked state appropriately wherever possible.
127 This form of `tracked` also accepts an optional configuration object
128 containing either an initial `value` or an `initializer` function (but not
129 both).
130
131 ```javascript
132 import EmberObject from '@ember/object';
133 import { tracked } from '@glimmer/tracking';
134
135 const Entry = EmberObject.extend({
136 name: tracked({ value: 'Zoey' }),
137 favoriteSongs: tracked({
138 initializer: () => ['Raspberry Beret', 'Time After Time']
139 })
140 });
141 ```
142
143 @method tracked
144 @static
145 @for @glimmer/tracking
146 @public
147 */
148 /**
149 The `@cached` decorator can be used on getters in order to cache the return
150 value of the getter. This is useful when a getter is expensive and used very
151 often. For instance, in this guest list class, we have the `sortedGuests`
152 getter that sorts the guests alphabetically:
153
154 ```js
155 import { tracked } from '@glimmer/tracking';
156
157 class GuestList {
158 @tracked guests = ['Zoey', 'Tomster'];
159
160 get sortedGuests() {
161 return this.guests.slice().sort()
162 }
163 }
164 ```
165
166 Every time `sortedGuests` is accessed, a new array will be created and sorted,
167 because JavaScript getters do not cache by default. When the guest list is
168 small, like the one in the example, this is not a problem. However, if the guest
169 list were to grow very large, it would mean that we would be doing a large
170 amount of work each time we accessed `sortedGetters`. With `@cached`, we can
171 cache the value instead:
172
173 ```js
174 import { tracked, cached } from '@glimmer/tracking';
175
176 class GuestList {
177 @tracked guests = ['Zoey', 'Tomster'];
178
179 @cached
180 get sortedGuests() {
181 return this.guests.slice().sort()
182 }
183 }
184 ```
185
186 Now the `sortedGuests` getter will be cached based on _autotracking_. It will
187 only rerun and create a new sorted array when the `guests` tracked property is
188 updated.
189
190 In general, you should avoid using `@cached` unless you have confirmed that the
191 getter you are decorating is computationally expensive. `@cached` adds a small
192 amount of overhead to the getter, making it more expensive. While this overhead
193 is small, if `@cached` is overused it can add up to a large impact overall in
194 your app. Many getters and tracked properties are only accessed once, rendered,
195 and then never rerendered, so adding `@cached` when it is unnecessary can
196 negatively impact performance.
197
198 @method cached
199 @static
200 @for @glimmer/tracking
201 @public
202 */
203}