declare module '@glimmer/tracking' { export { tracked, cached } from '@ember/-internals/metal'; /** In order to tell Ember a value might change, we need to mark it as trackable. Trackable values are values that: - Can change over their component’s lifetime and - Should cause Ember to rerender if and when they change We can do this by marking the field with the `@tracked` decorator. @module @glimmer/tracking @public */ /** Marks a property as tracked. By default, values that are rendered in Ember app templates are _static_, meaning that updates to them won't cause the application to rerender. Marking a property as tracked means that when that property changes, any templates that used that property, directly or indirectly, will rerender. For instance, consider this component: ```handlebars
Count: {{this.count}}
Times Ten: {{this.timesTen}}
``` ```javascript import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; import { action } from '@ember/object'; export default class CounterComponent extends Component { @tracked count = 0; get timesTen() { return this.count * 10; } @action plusOne() { this.count += 1; } } ``` Both the `{{this.count}}` and the `{{this.timesTen}}` properties in the template will update whenever the button is clicked. Any tracked properties that are used in any way to calculate a value that is used in the template will cause a rerender when updated - this includes through method calls and other means: ```javascript import Component from '@glimmer/component'; import { tracked } from '@glimmer/tracking'; class Entry { @tracked name; @tracked phoneNumber; constructor(name, phoneNumber) { this.name = name; this.phoneNumber = phoneNumber; } } export default class PhoneBookComponent extends Component { entries = [ new Entry('Pizza Palace', 5551234), new Entry('1st Street Cleaners', 5554321), new Entry('Plants R Us', 5552468), ]; // Any usage of this property will update whenever any of the names in the // entries arrays are updated get names() { return this.entries.map(e => e.name); } // Any usage of this property will update whenever any of the numbers in the // entries arrays are updated get numbers() { return this.getFormattedNumbers(); } getFormattedNumbers() { return this.entries .map(e => e.phoneNumber) .map(number => { let numberString = '' + number; return numberString.slice(0, 3) + '-' + numberString.slice(3); }); } } ``` It's important to note that setting tracked properties will always trigger an update, even if the property is set to the same value as it was before. ```js let entry = new Entry('Pizza Palace', 5551234); // if entry was used when rendering, this would cause a rerender, even though // the name is being set to the same value as it was before entry.name = entry.name; ``` `tracked` can also be used with the classic Ember object model in a similar manner to classic computed properties: ```javascript import EmberObject from '@ember/object'; import { tracked } from '@glimmer/tracking'; const Entry = EmberObject.extend({ name: tracked(), phoneNumber: tracked() }); ``` Often this is unnecessary, but to ensure robust auto-tracking behavior it is advisable to mark tracked state appropriately wherever possible. This form of `tracked` also accepts an optional configuration object containing either an initial `value` or an `initializer` function (but not both). ```javascript import EmberObject from '@ember/object'; import { tracked } from '@glimmer/tracking'; const Entry = EmberObject.extend({ name: tracked({ value: 'Zoey' }), favoriteSongs: tracked({ initializer: () => ['Raspberry Beret', 'Time After Time'] }) }); ``` @method tracked @static @for @glimmer/tracking @public */ /** The `@cached` decorator can be used on getters in order to cache the return value of the getter. This is useful when a getter is expensive and used very often. For instance, in this guest list class, we have the `sortedGuests` getter that sorts the guests alphabetically: ```js import { tracked } from '@glimmer/tracking'; class GuestList { @tracked guests = ['Zoey', 'Tomster']; get sortedGuests() { return this.guests.slice().sort() } } ``` Every time `sortedGuests` is accessed, a new array will be created and sorted, because JavaScript getters do not cache by default. When the guest list is small, like the one in the example, this is not a problem. However, if the guest list were to grow very large, it would mean that we would be doing a large amount of work each time we accessed `sortedGetters`. With `@cached`, we can cache the value instead: ```js import { tracked, cached } from '@glimmer/tracking'; class GuestList { @tracked guests = ['Zoey', 'Tomster']; @cached get sortedGuests() { return this.guests.slice().sort() } } ``` Now the `sortedGuests` getter will be cached based on _autotracking_. It will only rerun and create a new sorted array when the `guests` tracked property is updated. In general, you should avoid using `@cached` unless you have confirmed that the getter you are decorating is computationally expensive. `@cached` adds a small amount of overhead to the getter, making it more expensive. While this overhead is small, if `@cached` is overused it can add up to a large impact overall in your app. Many getters and tracked properties are only accessed once, rendered, and then never rerendered, so adding `@cached` when it is unnecessary can negatively impact performance. @method cached @static @for @glimmer/tracking @public */ }