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

Get missing reference identifier #641

Closed
TimHollies opened this issue Feb 6, 2018 · 11 comments
Closed

Get missing reference identifier #641

TimHollies opened this issue Feb 6, 2018 · 11 comments

Comments

@TimHollies
Copy link

Apologies if I've missed an obvious way to do this - but I'm a bit stuck.

I have a reference in a model which links to another model which is yet to be loaded. Attempting to dereference the property (correctly) throws an error. Is there an easy way to access the identifier the reference is looking for.

E.g. Attempting to access the property throws: Error: [mobx-state-tree] Failed to resolve reference of type EntityType: 'd584fa4e-fb7a-4875-8048-3870118fcdad'. I want to access the value d584fa4e-fb7a-4875-8048-3870118fcdad.

I've had a few ideas about how to do this:

  • Get a snapshot of the whole model and get the identifier out of the JSON object
  • Catch the error and parse the error message string
  • Create a custom reference and return the identifier as a string or throw an easy-to-parse error if the reference is not found

These all seem a bit overkill to retrieve a string value - is there an API function I'm missing?

@robinfehr
Copy link
Contributor

robinfehr commented Feb 6, 2018

what do you mean by yet to be loaded?
(currently i'm assuming you did serialize your state and try to load the snapshot)
could you also show us how you defined your references?

@TimHollies
Copy link
Author

I meant that the model with the identifier that the reference points to is not yet in the tree. Here's a toy example:

// setup types

const Person = types.model({
  id: types.identifier(types.string),
  name: types.string
});

const Cat = types.model({
  id: types.identifier(types.string),
  owner: types.reference(Person)
});

const Container = types.model({
  cat: Cat,
  person: Person
});

// database helper functions

const loadCat = (id) => { 
  //return Cat model 
};

const loadPerson = (id) => { 
  //return Person model 
};

// load cat from data store

/*
{
  id: "cat_id_1",
  owner: "person_id_1"
}
*/

const cat = await loadCat("cat_id_1");

// now load the owner

const person = await loadPerson(cat.owner); // throws

// create complete tree for use in program

const container = Container.create({
  cat, person
});

The workaround I am currently using is:

const person = await loadPerson(getSnapshot(cat).owner);

What I'm hoping for is that there's something like this which I'm missing:

const person = await loadPerson(cat.owner.reference);
// or 
const person = await loadPerson(getReference(cat.owner));

I would like to be able to get the string 'person_id_1' out of the 'cat' model without having to create a snapshot of the whole model.

I could get the db helper functions to return plain objects instead of models, but creating the models inside those functions means that they can have the responsibility of handling the error case where the database doesn't match the model (e.g. someone messed up running db migrations) rather than having to consider that case in every place those helpers are called.

Thanks for your help.

@robinfehr
Copy link
Contributor

robinfehr commented Feb 8, 2018

I think what you're looking for is this (custom references):

const UserByNameReference = types.maybe(

@TimHollies
Copy link
Author

I'm not sure that it is; I don't want to load the data, I just want to get the identifier the reference is looking for.

I've read through the source and come up with this:

const getReference = (model, property) =>
  model.$mobx.values[property].value.storedValue.value;

which seems to work. But I guess there are no guarantees that these property names won't change in future versions.

Is there any chance of adding such a function to the supported API?

@robinfehr
Copy link
Contributor

robinfehr commented Feb 9, 2018

i see - you’d need a getIdentifier(thing) fn exposed. we just had that request today :) we have it already internally and should expose it @mweststrate
#643

@mweststrate
Copy link
Member

@robinfehr agreed, let's expose it!

@mweststrate
Copy link
Member

Closed in favor of #674

@robinfehr
Copy link
Contributor

I'm sorry for not realising that earlier but what about using getSnapshot within afterCreate?

e.g.

const Person = types.model({
  id: types.identifier(types.string),
  name: types.string
});
const Cat = types.model({
  id: types.identifier(types.string),
  owner: types.reference(Person)
}).actions(self => ({
  afterCreate() {
     const {owner} = getSnapshot(self)
     ...
  }
});

@vmurillo
Copy link

vmurillo commented Mar 8, 2021

hey @robinfehr, I am facing a similar need. In my particular, I would like to set the reference on demand (not in afterCreate).

When I use getIdentifier with an invalid reference, I do get the identifier I was expecting. However, I also get this exception InvalidReferenceError. Do you know if it is possible to avoid the exception?

@patrickschmelter
Copy link

patrickschmelter commented Aug 9, 2022

The workaround I am currently using is:
const person = await loadPerson(getSnapshot(cat).owner);

@TimHollies workaround is the only thing that has worked for me. As @vmurillo mentioned, getIdentifier with a not yet loaded reference throws the exception.

@patrickschmelter
Copy link

To make things worse, the exception is unavoidable on a types.maybe reference. A huge try catch block is the only solution that I found

        try {
            // @ts-expect-error some type randomness here
            self.reference = data.reference !== undefined ? getSnapshot(data).reference : undefined;
        } catch (e) {
            if ((e as Error).message.includes('Failed to resolve reference')) {
                console.log((e as Error).message);
                console.log('will be there later');
            } else {
                throw e;
            }
        }

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

No branches or pull requests

5 participants