-
-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
_updatePromiseProxyFor throws "used previously in the same computation" error #7196
Comments
This looks like an interop issue with tracked properties and mirage. Don't have a ton of time to dig in but I'd guess the issue is related to mirage being an unrealistic async environment. |
@runspired thanks for the response. Unfortunately, that's not correct – I took the time to create this minimal reproduction cause I thought it would help, but it was an error I experienced in production with a live API. |
Since args are autotracked and the Proxy.set uses A few thoughts.
|
Thanks @snewcomer I'll file it with ember.js
Do you have any ideas about how to refactor / avoid this error? The only way I can think of is to perhaps enforce more precise timing in the data retrieval from the store, but that is not ideal |
The one piece that feels a little off track is the In this new world of Octane, working with pure
|
Thanks @snewcomer on one hand I understand the principles behind this, but on a practical level I struggle with this a little. When you say:
I'm not sure I understand this. AFAIK Also, what's wrong with relying on EmberArray "observability"? Isn't using a tracked EmberArray (e.g. https://guides.emberjs.com/release/upgrading/current-edition/tracked-properties/#toc_arrays) relying on that same "observability"? P Practically speaking, I'd expect DS.RecordArray to behave like a tracked EmberArray. If the records change, I'd expect any dependent getters to update. Here you are essentially saying we cannot treat RecordArrays like tracked properties – instead you have to manage the updating yourself. This defeats the whole point of a RecordArray with The reason I posted this here instead of in ember.js is because I thought it was a mismanagement of
From my limited knowledge, this seems like it's happening because on render, the That seems like an ember-data issue? |
You make a lot of good points.
Observable is inherently a
This was just to illuminate that I generally would move towards plain arrays if my use case afforded it (explicit DDAU w/ simple iteration). |
Thanks @snewcomer for explaining. I'll definitely have to read more into how tracking works. I've encountered this error in several other situations since, and unfortunately a production app is typically way too complex to be able to implement this kind of solution. Records are often not created / updated where they are rendered. Having a "single source of truth" Store is pretty much the point here – having to maintain tracked arrays everywhere defeats that purpose. However, if it was absolutely necessary to do that, I think the deprecated I've resorted to "de-syncing" the store fetches with the renders by adding 1ms timeout to fetch tasks. Not ideal, but prevents us from having to downgrade Ember. |
I spent some time today digging into this and I believe we should close this as "user error" (don't worry, I will explain below!). The issue was certainly tricky to spot, and @pzuraq may have some thoughts on whether there are any action items on behalf of the framework or learning team to make this less painful in the future. Root Cause On the controller we define an unstable getter. "Unstable" because every access of this getter will return a fully new array instance. get data() {
return this.store.peekAll("post").filterBy("user.id", "1");
} In the template, we render some values based on But then we added this get dataLength() {
return this.args.data.length;
} At this point an error is thrown if Here's the catch, passing the data prop via a template does not pass the value, but rather a wrapper that maintains the calling context. So accessing <Foo @data={{this.data}} /> Simple Fix Memoize the getter. get posts() {
return this.store.peekAll("post");
}
@computed("posts.length")
get data() {
return this.posts.filterBy("user.id", "1");
} More Complicated Considerations
|
After even deeper investigation I learned a few more things:
A simple user-land example of this would be to use |
I created glimmerjs/glimmer-vm#1111 to help out here a little bit, it seems from what I can tell glimmer-vm does do a better job memoizing in production. |
@runspired thanks, I'm obviously in over my head here when in comes to the internals, but I would like to understand the "simple fix" at least as an end user of the framework When you say to memoize the getter, do you mean that the usage of And in what situation is this required to avoid the "user error" here? Is it: "do not use getters when dealing with ember-data RecordArrays?", "use As mentioned, the use of mirage was only to demonstrate the error, but I was facing this all over my live production app when I upgraded from 3.14 to 3.18. If this is to be closed as "user error" I'd like to know what exactly the user error I made was, and what changes I have to make to my codebase exactly It's not obvious to me how get data() {
return this.store.peekAll("post").filterBy("user.id", "1");
} is an erroneous use of the framework... |
@wongpeiyi This fix was backported to 3.16.9. Mind checking to see if this error still is around? closed here - emberjs/ember.js#19003 |
@snewcomer it likely is still around due to the memoization issue with DEBUG templates in glimmer. I need to add another test to the PR so it gets merged. @wongpeiyi getters run on every access, so if they return a referentially unstable value (eg they make a new computation each time) then accessing that getter more than once in a render is an error. Similarly this is why in JS code the best practice would be to cache the value of the computation for reuse vs recalculating it every time you need access to it again. |
Closing this issue as there is nothing more for us to do but the changes in #glimmerjs/glimmer-vm#1111 are still relevant. |
When an API payload is received with an association not already set on the Ember model, the following error is seen:
This happens when the model is being consumed in a component template (via a getter) that depends on
post.user
Reproduction
https://github.com/wongpeiyi/data-computation
Description
filterBy('user.id')
, the error is thrownThe error is not thrown when:
{{@data.length}}
in the template instead of the component getterThis seems like a use case that should "just work"
Versions
Run the following command and paste the output below:
yarn list ember-source && yarn list ember-cli && yarn list --pattern ember-data
.The text was updated successfully, but these errors were encountered: