-
-
Notifications
You must be signed in to change notification settings - Fork 4.2k
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
[BUGFIX beta] Autotrack Modifiers and Helpers #18266
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Looking good, I left some inline comments / suggestions.
I think we should also make non-class based helpers auto-track, can you add that here as well?
managerAPI: '3.13', | ||
optionalFeatures: OptionalCapabilities | ||
): Capabilities { | ||
assert('Invalid modifier manager compatibility specified', managerAPI === '3.13'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a breaking change, we have to allow previously supported values for this
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah, I thought this was a bit weird. This is inconsistent with the component manager capabilities API, which asserts it must be a particular version. Happy to make the change here, but maybe we should also make the change there as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@pzuraq - No the component manager one is correct, the issue here is that we:
- don't require modifier managers to have specified capabilities (just a bug in initial implementation)
- any that did specify capabilities could have passed any arbitrary thing for the first argument
We should fix this mistake by doing:
- Adding a deprecation when we encounter a manager without capabilities set
- Deprecate calling this method (
capabilities
) with a value other than 3.13 (for the new flag)
export function capabilities(_managerAPI: string, _optionalFeatures?: {}): Capabilities { | ||
return {}; | ||
export function capabilities( | ||
managerAPI: '3.13', |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We need to allow whatever version introduced managers, and we should probably add a deprecation for custom managers not having their capabilities set (nearly all of the managers in existence that I can find do not set capabilities at all)
assert('Invalid modifier manager compatibility specified', managerAPI === '3.13'); | ||
|
||
return { | ||
disableLifecycleTracking: Boolean(optionalFeatures.disableLifecycleTracking), |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This name does not match up at all with "auto tracking" to me, "disable lifecycle tracking" in the abstract seems like it would be more about handling teardown?
Some suggestions:
disableTracking
disableTrackedPropertyTracking
disableAutoTracking
delegate.installModifier(modifier, element, args.value()); | ||
let { element, args, delegate, modifier, tag } = state; | ||
|
||
let tracked = track(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would think this should be guarded as well?
let { capabilities } = delegate;
let shouldAutoTrack = capabilities === undefined || capabilities.disableLifecycleTracking !== true;
let tracked;
if (shouldAutoTrack) {
tracked = track(() => {
delegate.installModifier(modifier, element, args.value());
});
} else {
delegate.installModifier(modifier, element, args.value());
}
if (tracked !== undefined) {
update(tag, tracked);
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wrapping it in a track
call prevents external track
s from watching the values. My logic was that if you disabled tracking, you probably don't want anything to be able to track these computes, so we should swallow any autotracking within and throw it away. We can use the untrack
function for this though.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
you probably don't want anything to be able to track these computes, so we should swallow any autotracking within and throw it away
Hmm, I see. I think both are valid paths forward, definitely up to you here. Though if you don't mind, could you add a small inline comment about throwing away the tracker in the case where you have explicitly opted out?
delegate.updateModifier(modifier, args.value()); | ||
let { args, delegate, modifier, tag } = state; | ||
|
||
let tracked = track(() => { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can we pick a better name for these tracked
variables? I find it conceptually confusing to have a variable named tracked
that is something different than @tracked
. Maybe combinedTrackingTag
or consumedTrackedPropertiesTag
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeahhhh, naming is hard lol, but yeah I'll go with the longer name
We already had tests for those: ember.js/packages/@ember/-internals/glimmer/tests/integration/helpers/tracked-test.js Lines 12 to 214 in 98b4aa9
|
Hmm, maybe I am mis-reading those but they do not look like what I was thinking. I mean specifically a test where a non-class based helper accesses a tracked value within its computation function. All of the tests that you linked seemed to be asserting that the helper will be reevaluated if an input to it was derived from a tracked property. '@test asdfasdfasdfasdfasdfasdfasdf'(assert) {
let computeCount = 0;
let currentUserService;
this.registerService('current-user', Service.extend({
name: tracked({ value: 'bob' }),
init() {
this._super(...arguments);
currentUserService = this;
}
});
this.registerComponent('person', {
ComponentClass: Component.extend({
currentUser: inject('current-user')
}),
template: strip`
{{hello-world this.currentUser}}
`,
});
this.registerHelper('hello-world', ([service]) => {
computeCount++;
return service.name;
});
this.render('<Person/>');
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
runTask(() => this.rerender());
this.assertText('bob-value');
assert.strictEqual(computeCount, 1, 'compute is called exactly 1 time');
runTask(() => currentUserService.name = 'sal');
this.assertText('sal-value');
assert.strictEqual(computeCount, 2, 'compute is called exactly 2 times');
} |
ah, gotcha, yeah that makes sense to add, will do 👍 |
98b4aa9
to
03f8fe4
Compare
Alright, updated based on feedback, plus I added ember-cli/ember-rfc176-data#166 to actually expose the modifier manager capabilities generator |
This PR adds autotracking for modifiers and class based helpers. Modifiers can opt out of autotracking via a new capabilities flag on modifier managers.
03f8fe4
to
b8a5633
Compare
emberjs/ember.js#18266 expects a capabilities config, and throws a deprecation when that is not provided. This change add support for that, thus silencing the deprecation. Also imports proper RFC176-based modules for modifier APIs, supported by ember-cli-babel 7.10
This PR adds autotracking for modifiers and class based helpers.
Modifiers can opt out of autotracking via a new capabilities flag on
modifier managers.