-
-
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
[IE11] @tracked property initializer is not applied #18075
Comments
I added another test case, showing that a re-render is properly triggered nevertheless, when setting the property:
import { module, test } from 'qunit';
import { setupRenderingTest } from 'ember-qunit';
import { render, settled } from '@ember/test-helpers';
import hbs from 'htmlbars-inline-precompile';
import { tracked } from '@glimmer/tracking';
module('Integration | @tracked', function(hooks) {
setupRenderingTest(hooks);
test('it re-renders, when the value is mutated', async function(assert) {
class Subject {
@tracked
initializedProp = 'foo';
@tracked
uninitializedProp;
}
this.subject = new Subject();
await render(hbs`
initializedProp = {{this.subject.initializedProp}};
uninitializedProp = {{this.subject.uninitializedProp}};
`);
// Only this first assertion fails in IE11, the rest passes.
assert.ok(this.element.textContent.includes('initializedProp = foo;'));
assert.ok(this.element.textContent.includes('uninitializedProp = ;'));
this.subject.initializedProp = 'bar';
await settled();
assert.ok(this.element.textContent.includes('initializedProp = bar;'));
assert.ok(this.element.textContent.includes('uninitializedProp = ;'));
this.subject.uninitializedProp = 'qux';
await settled();
assert.ok(this.element.textContent.includes('initializedProp = bar;'));
assert.ok(this.element.textContent.includes('uninitializedProp = qux;'));
});
}); |
ember.js/packages/@ember/-internals/metal/lib/tracked.ts Lines 203 to 222 in be4f59a
When first accessing a
|
Something is really screwed up in IE11. Calling Somehow they all must be sharing the same base object that |
Indeed! For whatever reason, this line is included in the polyfill and sets the symbol on // L143
if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set }); // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
var setSymbolDesc = DESCRIPTORS && $fails(function () {
return _create(dP({}, 'a', {
get: function () { return dP(this, 'a', { value: 7 }).a; }
})).a != 7;
}) ? function (it, key, D) {
var protoDesc = gOPD(ObjectProto, key);
if (protoDesc) delete ObjectProto[key];
dP(it, key, D);
if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);
} : dP;
I don't know why that is (probably just an IE quirk), but I expect |
IMO this is a bug upstream, but I don't have high hopes, that it will (or even can) be fixed (in a timely manner). Should we maybe workaround it instead? |
We can change the implementation to avoid relying on setting arbitrary properties on the object. Something like: function descriptorForField([_target, key, desc]: [
object,
string,
DecoratorPropertyDescriptor
]): DecoratorPropertyDescriptor {
assert(
`You attempted to use @tracked on ${key}, but that element is not a class field. @tracked is only usable on class fields. Native getters and setters will autotrack add any tracked fields they encounter, so there is no need mark getters and setters with @tracked.`,
!desc || (!desc.value && !desc.get && !desc.set)
);
let initializer = desc ? desc.initializer : undefined;
let secretKey = symbol(key);
let VALUES: new WeakSet();
return {
enumerable: true,
configurable: true,
get(): any {
let propertyTag = tagForProperty(this, key);
if (CURRENT_TRACKER) CURRENT_TRACKER.add(propertyTag);
let value = undefined;
// If the field has never been initialized, we should initialize it
if (!VALUES.has(this)) {
if (typeof initializer === 'function') {
value = initializer.call(this);
}
VALUES.set(this, value);
}
// Add the tag of the returned value if it is an array, since arrays
// should always cause updates if they are consumed and then changed
if (Array.isArray(value) || isEmberArray(value)) {
update(propertyTag, tagForProperty(value, '[]'));
}
return value;
},
set(newValue: any): void {
markObjectAsDirty(this, key);
VALUES.set(this, newValue);
if (propertyDidChange !== null) {
propertyDidChange();
}
},
};
} |
We decided on the symbol based approach for performance reasons, since accessing a WeakMap would likely be pretty slow compared to a direct slot on the object. If we want to avoid arbitrary properties on the object in development mode for better DX I think that would be reasonable, but I do think we should continue with the symbol based approach for prod. We also were planning on creating a Babel transform specifically for |
I'd be pretty surprised if the WeakMap solution has different performance characteristics than the Either way, we have to support IE11 and the core-js symbol stuff that is linked above is pretty odd (all generated symbols are defined on |
Maybe @krisselden can chime in here, this was based on our conversations with him and his experience with WeakMaps vs direct slots. We did switch to the I actually think the solution of adding the babel transform would fix the problem here, since we don't actually want to assign values from the initializer lazily, even if we go with the |
Assuming that we want to stick with the usage of symbols and want to go with the Babel transform, what you'd envision is a transform that basically does this...? class Foo {
@tracked
bar = 'qux';
} becomes class Foo {
@tracked
bar;
constructor() {
this.bar = 'qux';
}
} |
Yup, exactly! I think it would have to work on the class itself so it could get a lock on the whole class body, and it would need to be one of the first transforms to run (before class fields and decorators), but it should be a pretty simple transform to write. |
Coincidentally I had to do something very similar in machty/ember-concurrency-decorators#50. I'll try to spend some time on a PoC later. |
@pzuraq - Why do we have to eagerly initialize the values when using the WeakMap solution I suggested above? I understand that it is more spec compliant, but thats ultimately a babel issue (with the current plugin) not something we should have to work around in a special way. Specifically, if we are using a WeakMap we don't have any shaping issues at all. The only shaping issues we currently might have is because we are adding more fields to the object. |
Made a Babel transform here: It's probably far from perfect, but IE11 works now! 🎉 |
@rwjblue I was mainly thinking to avoid the @buschtoens this looks great! I think this would be perfect, assuming we want to go this direction 😄 |
FWIW I think the symbol descriptor gets set on |
@pzuraq right, my point is that with a I really think the idiomatic / correct path forward here is the WeakMap solution I linked to above. |
From an architectural POV I would also prefer the IIRC the motivation to use symbols in the first place was that they apparently perform better than a |
@buschtoens thanks for your work on this, I just hit this same bug and was able to use your babel plugin as a quick workaround. It didn't run out of the box, I ended up copying the whole file out of the unreleased version of |
You likely have some derived classes, which run into this branch: if (isDerived) {
const bareSupers = [];
constructor.traverse(findBareSupers, bareSupers);
for (const bareSuper of bareSupers) {
bareSuper.insertAfter(nodes);
} I didn't copy over the |
@rwjblue @pzuraq In any case, if in the end you want to stick with symbols, I am happy to add tests around this transform, upstream it somewhere official and yield control / ownership to the If you want to go with the |
FYI - I chatted a bit more with @pzuraq and @krisselden, and I think we are all on the same page now. We are going to swap to the |
Submitted #18091 to migrate to the WeakMap path. |
In IE11 the property initializers for the
@tracked
decorator are not applied. The following test succeeds in Chrome, but fails in IE11.tests/unit/tracked-test.js
Reproduction repo here:
buschtoens/repro-tracked-ie-error
The text was updated successfully, but these errors were encountered: