Skip to content

Commit a4e7607

Browse files
committed
Add Array deprecation guides
1 parent fb67e7b commit a4e7607

File tree

3 files changed

+355
-0
lines changed

3 files changed

+355
-0
lines changed
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
---
2+
title: Deprecation of @ember/array Foundation APIs
3+
until: "6.0"
4+
since: "5.8"
5+
displayId: array.foundations
6+
---
7+
8+
The foundational APIs of `@ember/array`, including the `A()` function and the core mixins (`EmberArray`, `MutableArray`, `NativeArray`), are deprecated. These were used to create and extend arrays with Ember's observability. The modern approach is to use native JavaScript arrays, and `TrackedArray` from `tracked-built-ins` when reactivity is needed.
9+
10+
### `A()` Function and Core Mixins
11+
12+
The `A()` function would wrap a native array, making it an `EmberArray`. The `EmberArray` and `MutableArray` mixins could be used to build custom array-like classes.
13+
14+
**Before**
15+
```javascript
16+
import { A } from '@ember/array';
17+
import EmberObject from '@ember/object';
18+
import { MutableArray } from '@ember/array';
19+
20+
let emberArr = A([1, 2, 3]);
21+
emberArr.pushObject(4);
22+
23+
const MyArray = EmberObject.extend(MutableArray, {
24+
// ... implementation ...
25+
});
26+
```
27+
28+
**After**
29+
Use native arrays for standard array operations. For arrays that need to be tracked for reactivity in components and other classes, use `TrackedArray` from the `tracked-built-ins` addon.
30+
31+
```javascript
32+
// For a standard array
33+
let nativeArr = [1, 2, 3];
34+
nativeArr.push(4);
35+
36+
// For a tracked array
37+
import { TrackedArray } from 'tracked-built-ins';
38+
let trackedArr = new TrackedArray([1, 2, 3]);
39+
trackedArr.push(4); // This mutation is tracked
40+
```
41+
42+
### Utility Functions: `isArray` and `makeArray`
43+
44+
These functions helped create and check for arrays.
45+
46+
**Before**
47+
```javascript
48+
import { isArray, makeArray } from '@ember/array';
49+
let isArr = isArray([]);
50+
let ensuredArr = makeArray('hello');
51+
```
52+
53+
**After**
54+
Use native JavaScript equivalents.
55+
56+
```javascript
57+
// isArray() -> Array.isArray()
58+
let isArr = Array.isArray([]);
59+
60+
// makeArray() -> custom helper or ensure data is correct
61+
function ensureArray(value) {
62+
if (value === null || value === undefined) return [];
63+
return Array.isArray(value) ? value : [value];
64+
}
65+
let ensuredArr = ensureArray('hello');
66+
```
67+
68+
### Creating Custom Arrays with Proxies
69+
70+
For advanced use cases where you might have created a custom class based on `MutableArray` to add special behaviors to your array, the modern JavaScript equivalent is to use a `Proxy`. A `Proxy` object allows you to intercept and redefine fundamental operations for a target object (like an array), enabling you to create powerful custom wrappers.
71+
72+
**Example: A Logging Array**
73+
74+
Imagine you want to log every time an item is pushed to an array.
75+
76+
**Before**, you might have done this with `MutableArray`:
77+
78+
```javascript
79+
import EmberObject from '@ember/object';
80+
import { MutableArray } from '@ember/array';
81+
82+
const LoggingArray = EmberObject.extend(MutableArray, {
83+
// Internal content array
84+
_content: null,
85+
86+
init() {
87+
this._super(...arguments);
88+
this._content = this._content || [];
89+
},
90+
91+
// Required primitives
92+
objectAt(idx) { return this._content[idx]; },
93+
get length() { return this._content.length; },
94+
95+
// Override replace to add logging
96+
replace(idx, amt, objects) {
97+
if (amt === 0) {
98+
console.log(`Adding items: ${objects.join(', ')}`);
99+
}
100+
this._content.splice(idx, amt, ...objects);
101+
this.arrayContentDidChange(idx, amt, objects.length);
102+
}
103+
});
104+
105+
let arr = LoggingArray.create({ _content: [1, 2] });
106+
arr.pushObject(3); // Logs: "Adding items: 3"
107+
```
108+
109+
**After**, you can achieve the same result more cleanly by wrapping a `TrackedArray` in a `Proxy`. This allows you to add custom behavior while preserving the reactivity provided by `TrackedArray`.
110+
111+
```javascript
112+
import { TrackedArray } from 'tracked-built-ins';
113+
114+
function createTrackedLoggingArray(initialItems) {
115+
// Start with a TrackedArray instance
116+
const trackedArr = new TrackedArray(initialItems);
117+
118+
return new Proxy(trackedArr, {
119+
get(target, prop, receiver) {
120+
// Intercept the 'push' method
121+
if (prop === 'push') {
122+
return function(...args) {
123+
console.log(`Adding items via push: ${args.join(', ')}`);
124+
// Call the original push method on the TrackedArray
125+
// This will trigger reactivity automatically.
126+
return target.push(...args);
127+
}
128+
}
129+
130+
// Forward all other property access and method calls to the TrackedArray
131+
const value = Reflect.get(target, prop, receiver);
132+
return typeof value === 'function' ? value.bind(target) : value;
133+
},
134+
135+
set(target, prop, value, receiver) {
136+
// Intercept direct index assignment
137+
if (!isNaN(parseInt(prop, 10))) {
138+
console.log(`Setting index ${prop} to ${value}`);
139+
}
140+
// Forward the set operation to the TrackedArray to trigger reactivity
141+
return Reflect.set(target, prop, value, receiver);
142+
}
143+
});
144+
}
145+
146+
// In a component:
147+
class MyComponent {
148+
loggingArray = createTrackedLoggingArray([1, 2]);
149+
150+
addItem() {
151+
this.loggingArray.push(3); // Logs and triggers an update
152+
}
153+
154+
updateItem() {
155+
this.loggingArray[0] = 'new value'; // Logs and triggers an update
156+
}
157+
}
158+
```
159+
160+
This `Proxy` approach is very powerful. By wrapping a `TrackedArray`, you can layer in custom logic while letting it handle the complexities of reactivity. This is the recommended pattern for creating advanced, observable array-like objects in modern Ember.
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
---
2+
title: Deprecation of @ember/array Read Methods
3+
until: "6.0"
4+
since: "5.8"
5+
displayId: array.read-methods
6+
---
7+
8+
All read-only methods and computed properties from `@ember/array` are deprecated. You should use native JavaScript array methods and properties instead. This guide covers the common read-only APIs and their native equivalents.
9+
10+
### `firstObject` and `lastObject`
11+
12+
These computed properties provided safe access to the first and last elements of an array.
13+
14+
**Before**
15+
```javascript
16+
import { A } from '@ember/array';
17+
let arr = A(['a', 'b', 'c']);
18+
let first = arr.get('firstObject'); // 'a'
19+
let last = arr.get('lastObject'); // 'c'
20+
```
21+
22+
**After**
23+
Use native array bracket notation, or the `at()` method for accessing elements from the end of the array.
24+
25+
```javascript
26+
let arr = ['a', 'b', 'c'];
27+
let first = arr[0];
28+
let last = arr.at(-1);
29+
```
30+
31+
### `objectAt` and `objectsAt`
32+
33+
These methods provided safe, index-based access to array elements.
34+
35+
**Before**
36+
```javascript
37+
import { A } from '@ember/array';
38+
let arr = A(['a', 'b', 'c']);
39+
let middle = arr.objectAt(1); // 'b'
40+
let some = arr.objectsAt([0, 2]); // ['a', 'c']
41+
```
42+
43+
**After**
44+
Use native array bracket notation for `objectAt`. For `objectsAt`, you can use `map`.
45+
46+
```javascript
47+
let arr = ['a', 'b', 'c'];
48+
let middle = arr[1];
49+
let some = [0, 2].map(index => arr[index]);
50+
```
51+
52+
### `mapBy`, `filterBy`, `rejectBy`, `findBy`
53+
54+
These methods were shortcuts for common mapping and filtering operations on arrays of objects.
55+
56+
**Before**
57+
```javascript
58+
import { A } from '@ember/array';
59+
let users = A([
60+
{ name: 'John', isActive: true },
61+
{ name: 'Jane', isActive: false },
62+
]);
63+
let names = users.mapBy('name');
64+
let active = users.filterBy('isActive', true);
65+
let john = users.findBy('name', 'John');
66+
```
67+
68+
**After**
69+
Use the native `map`, `filter`, and `find` methods with arrow functions.
70+
71+
```javascript
72+
let users = [
73+
{ name: 'John', isActive: true },
74+
{ name: 'Jane', isActive: false },
75+
];
76+
let names = users.map(user => user.name);
77+
let active = users.filter(user => user.isActive === true);
78+
let john = users.find(user => user.name === 'John');
79+
```
80+
81+
### `uniqBy`
82+
83+
`uniqBy` created a new array with unique elements based on a property.
84+
85+
**Before**
86+
```javascript
87+
import { uniqBy } from '@ember/array';
88+
let users = [{ id: 1 }, { id: 2 }, { id: 1 }];
89+
let unique = uniqBy(users, 'id');
90+
```
91+
92+
**After**
93+
Use a `Map` to efficiently create a unique list.
94+
95+
```javascript
96+
let users = [{ id: 1 }, { id: 2 }, { id: 1 }];
97+
let unique = Array.from(
98+
users.reduce((map, user) => map.set(user.id, user), new Map()).values()
99+
);
100+
```
101+
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
---
2+
title: Deprecation of @ember/array Write Methods
3+
until: "6.0"
4+
since: "5.8"
5+
displayId: array.write-methods
6+
---
7+
8+
All methods from `@ember/array` that mutate or "write" to an array are deprecated. This includes observable methods like `pushObject`.
9+
10+
The modern approach is to use `TrackedArray` from the [`tracked-built-ins`](https://github.com/tracked-tools/tracked-built-ins) addon. This class provides a tracked version of the native JavaScript `Array` that can be mutated directly, and these mutations will be tracked automatically.
11+
12+
First, install the addon:
13+
14+
```bash
15+
ember install tracked-built-ins
16+
```
17+
18+
### Observable Write Methods
19+
20+
Methods like `pushObject`, `popObject`, `removeObject`, `insertAt`, and `removeAt` were used to modify arrays in a way that Ember's classic observability system could track.
21+
22+
**Before**
23+
```javascript
24+
import { A } from '@ember/array';
25+
let arr = A([1, 2, 3]);
26+
27+
arr.pushObject(4);
28+
arr.removeAt(1, 1); // remove 1 item at index 1
29+
```
30+
31+
**After**
32+
Use `TrackedArray` and mutate it directly with standard JavaScript array methods. Using `TrackedArray` provides an ergonomic API that is nearly identical to working with plain JavaScript arrays, while providing the reactivity needed for your application's UI to update automatically.
33+
34+
```javascript
35+
import { TrackedArray } from 'tracked-built-ins';
36+
37+
// In a component or class
38+
class MyComponent {
39+
myArray = new TrackedArray([1, 2, 3]);
40+
41+
addItem() {
42+
// pushObject -> push
43+
this.myArray.push(4);
44+
}
45+
46+
removeItem() {
47+
// removeAt -> splice
48+
this.myArray.splice(1, 1);
49+
}
50+
51+
clearItems() {
52+
// clear -> set length to 0
53+
this.myArray.length = 0;
54+
}
55+
}
56+
```
57+
58+
This pattern applies to all mutation methods. Here is a brief mapping for the most common methods:
59+
60+
| `@ember/array` Method | Native `TrackedArray` Method |
61+
|-----------------------|------------------------------|
62+
| `pushObject(s)` | `push` / `...` (spread) |
63+
| `popObject()` | `pop` |
64+
| `shiftObject()` | `shift` |
65+
| `unshiftObject(s)` | `unshift` |
66+
| `insertAt(idx, obj)` | `splice(idx, 0, obj)` |
67+
| `removeAt(idx, len)` | `splice(idx, len)` |
68+
| `clear()` | `length = 0` |
69+
| `replace()` | `splice` |
70+
71+
### Handling Uniqueness and Specific Objects
72+
73+
For methods like `addObject` and `removeObject` that deal with specific object instances or uniqueness, you need a bit more logic.
74+
75+
```javascript
76+
// In your component class with `myArray = new TrackedArray([...])`
77+
78+
// removeObject replacement
79+
removeItem(item) {
80+
const index = this.myArray.indexOf(item);
81+
if (index > -1) {
82+
this.myArray.splice(index, 1);
83+
}
84+
}
85+
86+
// addObject replacement
87+
addUniqueItem(item) {
88+
if (!this.myArray.includes(item)) {
89+
this.myArray.push(item);
90+
}
91+
}
92+
```
93+
94+
Alternatively, if you are working with a list that must be unique, consider using a `Set` or `TrackedSet` from `tracked-built-ins`, as they handle uniqueness automatically.

0 commit comments

Comments
 (0)