Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add deprecation guide for array prototype extensions for V5 #1192

Open
wants to merge 11 commits into
base: main
Choose a base branch
from

Conversation

smilland
Copy link

@smilland smilland commented Sep 1, 2022

RFC: Deprecate array prototype extensions

Rendered content

It's ready for review, though only can be merge after the RFC is approved.

@smilland smilland changed the title Add deprecate guide for array prototype extensions for V5 Add deprecation guide for array prototype extensions for V5 Sep 1, 2022
@netlify
Copy link

netlify bot commented Sep 1, 2022

Deploy Preview for ember-deprecations ready!

Name Link
🔨 Latest commit 814dfca
🔍 Latest deploy log https://app.netlify.com/sites/ember-deprecations/deploys/64262c22b8f7810008071392
😎 Deploy Preview https://deploy-preview-1192--ember-deprecations.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify site settings.

content/ember/v5/deprecate-array-prototype-extensions.md Outdated Show resolved Hide resolved
After:
```js
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.any(el => el.isFruit); // true

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Array doesn't have .any() method. I think .some() is appropriate here.

Suggested change
someArray.any(el => el.isFruit); // true
someArray.some(el => el.isFruit); // true

content/ember/v5/deprecate-array-prototype-extensions.md Outdated Show resolved Hide resolved
? a.food?.localCompare(b.food)
: a.isFruit - b.isFruit;
}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In general, I think that it would have to be stated here that this implementation is not as robust as Ember Array's .sortBy. It has field names hardcoded and can't take an arbitrary number of field names.

Comment on lines +232 to +242
Before:
```js
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
someArray.toArray(); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
```

After:
```js
const someArray = [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }];
[...someArray] // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
```

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I never used this method but the docs are saying:

Simply converts the object into a genuine array. The order is not guaranteed. Corresponds to the method implemented by Prototype.

So I guess an example should show how to convert an object into an array. I'd have to check how the original function works but would this be expected?

const person = {
    firstName: 'John',
    lastName: 'Doe'
};

Object.entries(person); // [ [ 'firstName', 'John' ], [ 'lastName', 'Doe' ] ]

🤔

content/ember/v5/deprecate-array-prototype-extensions.md Outdated Show resolved Hide resolved
@smilland
Copy link
Author

thank you so much for reviewing @tniezurawski !


@action
pushObject(value) {
this.abc = [...this.abc, value];

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm a little confused by the difference between this example and the pushObject example above which uses TrackedArray. Is it a requirement to assign to this.abc when using @tracked but not when using TrackedArray? Or is the different method shown to indicate that both approaches are valid?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, @tracked will only trigger the reactivity when a new value gets assigned. When the existing native array is internally mutated, like calling .push(), then this will not be reactive. In opposite to TrackedArray, which does this kind of deep tracking. So I think the example is right.


@action
pushObjects(values) {
this.abc.splice(this.abc.length, 0, ...values);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would this.abc.push(...values) work here? It would be simpler and match the first after example for pushObject above?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i think so!

Copy link
Contributor

@simonihmig simonihmig left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lots of details in that migration guide, great work @smilland! I know this has been quite a whole since you worked on this, so would you still be available to look into and work on the provided suggestions?

The RFC was accepted long ago, so I think we can and should get this merged asap, as I would like to also get the actual deprecation implemented before we miss the time window for the v6 release!

}
}

[new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
[new Person('Tom'), new Person('Joe')].map(person => person['greet']?.('Hi')); // ['Hi Tom', 'Hi Joe']
[new Person('Tom'), new Person('Joe')].forEach(person => person.greet('Hi')); // ['Hi Tom', 'Hi Joe']

I think forEach is more appropriate than map here, since we don't use the value returned by map. Also the method call can be simplified as suggested I believe? invoke might do that optional chaining internally, but if people were to rewrite that code to native, I think this is what they would/should write.

: a.isFruit - b.isFruit;
}); // [{ food: 'apple', isFruit: true }, { food: 'beans', isFruit: false }]
```
#### `toArray`
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this example even needed? I think you would use toArray an an array-like object like A(), to turn it into a native array. But here we are only dealing with native arrays, so what's the point of toArray() usage then?

Comment on lines +254 to +255
const someArray = ['a', 'a', 'b', 'b']
[...new Set(someArray)] // ['a', 'b']
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This example should match the one above, so rather like:

Suggested change
const someArray = ['a', 'a', 'b', 'b']
[...new Set(someArray)] // ['a', 'b']
const someArray = [1, 2, 3, undefined, 3];
[...new Set(someArray)] // [1, 2, 3, undefined]

id: deprecate-array-prototype-extensions
title: "Deprecate array prototype extensions"
until: '6.0.0'
since: '5.0.0'
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we update this to the current one on main, assuming we get the deprecation implemented in that version?

Suggested change
since: '5.0.0'
since: '5.10.0'

import { TrackedArray } from 'tracked-built-ins';

export default class SampleComponent extends Component {
abc = new TrackedArray();
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
abc = new TrackedArray();
abc = new TrackedArray(['x', 'y', 'z', 'x']);


@action
pushObject(value) {
this.abc = [...this.abc, value];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, @tracked will only trigger the reactivity when a new value gets assigned. When the existing native array is internally mutated, like calling .push(), then this will not be reactive. In opposite to TrackedArray, which does this kind of deep tracking. So I think the example is right.


@action
pushObjects(values) {
this.abc.splice(this.abc.length, 0, ...values);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, i think so!

Comment on lines +826 to +827
this.abc.splice(start, len);
this.abc = [...this.abc];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this would also work, and be a bit simpler?

Suggested change
this.abc.splice(start, len);
this.abc = [...this.abc];
this.abc.splice(start, len);
this.abc = this.abc;

Comment on lines +858 to +865
let loc = this.abc.length || 0;
while (--loc >= 0) {
let curValue = this.abc.at(loc);

if (curValue === value) {
this.abc.splice(loc, 1);
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wouldn't a simple filer call work here?

Suggested change
let loc = this.abc.length || 0;
while (--loc >= 0) {
let curValue = this.abc.at(loc);
if (curValue === value) {
this.abc.splice(loc, 1);
}
}
this.abc.filter(item => item !== value);

same for the related examples below...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants