0
votes

I have an application for an RPG crafting system. I have one route for the user's current inventory (Inventory route), and another route for the user to collect items and eventually put them in their inventory (this is called the Gather route). I see this error any time I navigate from the Inventory route to the Gather route or vice versa. It seems to have something to do with the Item models I'm loading in the Gather route's model hook:


> Uncaught (in promise) Error: Assertion Failed: You attempted to update
> `length` on `item:common_curative_reagent
> (@ember-data:lid-item-common_curative_reagent),item:common_poisonous_reagent
> (@ember-data:lid-item-common_poisonous_reagent),(...)`, but it had
> already been used previously in the same computation.  Attempting to
> update a value after using it in a computation can cause logical
> errors, infinite revalidation bugs, and performance issues, and is not
> supported.

        
`length` was first used:

Stack trace for the update:
    at dirtyTagFor (validator.js:575)
    at markObjectAsDirty (index.js:546)
    at notifyPropertyChange (index.js:584)
    at arrayContentDidChange (index.js:691)
    at replaceInNativeArray (index.js:759)
    at Array.replace (array.js:1730)
    at Array.pushObjects (array.js:1452)

From what I've researched about the infinite revalidation error, I think it has something to do with a Glimmer tracked property being referenced in the same computation that can also modify it. It seems like the tracked property in this case is a list of Items I'm loading in the Gather route (the list of items it's possible for the user to collect), but this list must live somewhere in the framework that I don't have access to?

Inventory model hook:

async model()
{
    let user = this.session.getUser();
    let inventory = await this.inventory.getInventory();

    if (isEmpty(inventory?.ingredients)) { return []; }

    let items = RSVP.all(Object.entries(inventory.ingredients).map(([itemID]) => { return this.store.findRecord('item', itemID) }));

    let model = RSVP.hash({
        "items": items,
        "user": user,
        "inventory": inventory
    });

    return model;
}

The error seems to be related to the following code, because it doesn't happen if I comment this out and return an empty list. Gather model hook:

async loadGatherItems(gatherAry)
{
    // Search through the gather table columns and get a list of all possible items
    let promises = [];
    gatherAry.toArray().forEach((gatherRecord) =>
    {
        if (isEmpty(gatherRecord?.items)) { return; }
        gatherRecord.items.forEach((itemGroup) =>
        {
            if (isEmpty(itemGroup?.item)) { return; }
            let itemID = itemGroup.item;
            promises.push(this.store.findRecord("item", itemID));
        });
    });
    
    let gatherItems = await RSVP.all(promises);
    
    // Also include any 'trivial' item
    let trivials = await this.store.query("item", {
            "orderBy": "rarity",
            "equalTo": 'trivial'
        });
    trivials.forEach((item) => { gatherItems.push(item); });

    return gatherItems;
}

The application itself seems to work fine despite the error. Like I said, it doesn't happen when I load the Inventory or Gather routes respectively, but only when I switch from one to the other. It only happens once (e.g. if I keep switching back and forth, I'll only see the error once).

If I load the Items route before switching between those two routes, the error doesn't happen... the Items route does a findAll on the Item model. In fact, if I add a findAll on items at the top of my Inventory route, the error doesn't happen. I don't want to keep that, though, because I don't need all possible Items loaded in my inventory route - I only need the items in my Inventory.

I'm still learning both Ember and JS so any help explaining why this is happening would be greatly appreciated!

Edit 1

I still don't quite know why this error is happening, but it's definitely something to do with the way I'm loading Items into the store. When I switch routes, any new Items I'm getting with findRecord cause the error to appear - if the Item is already in the store, it doesn't cause the error to appear. That's why doing a findAll at the top of my model hook causes the error to go away.

1

1 Answers

0
votes

Ultimately, the error comes from calling findRecord on more than one ID which isn't already loaded in the store. Instead of calling findRecord in a loop, I should try and make some sort of single network query to get as small of an inclusive list as possible, and then pare it down client-side.

I don't know the best practices around this, or the specifics of why the error is happening (or whether it should happen at all?). On the one hand, I'm trying to limit the amount of data coming back from the server by only making requests for the IDs I know I need. On the other hand, I'm trading off a smaller dataset size with a much greater number of requests.

Maybe it's just a limitation of my backend that it doesn't support a query on a set of specific IDs (firebase).