Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error: [mobx-state-tree] Failed to resolve reference XXX to type 'User'

Context

I have a React application that is backed by mobx-state-tree (MST). One of the pages makes two parallel API calls to fetch a list of Projects and a list of system Users.

The responses of these two APIs are then applied as snapshot to two sibling nodes in the same tree. The Project model's owner field refers to a User model.

My problem is that the GET project API call often completes earlier than the GET Users API call. Classic "race condition". So, when MST tries to dereference an owner value, the user with such identifier is not available in the tree and I'm getting a run time error like this:

Error: [mobx-state-tree] Failed to resolve reference 'dtGNbVdNOQIAX0OMGaH2r2oSEf2cMSk9AJzPAf4n7kA' to type 'User' (from node: /projectListStore/projects/0/owner)

Question

How do I work around the error?

Please notice that making the API calls depend on each other (await or users.load().then(users => { projects.load(); }) are not an option for our project.

MST Models

export const ProjectModel = types
  .model('Project', {
    projectId: types.optional(types.identifierNumber, 0),
    title: '',
    descriptionStatus: '',
    projectStatus: '',
    startDate: CustomDate,
    endDate: CustomDate,
    owner: types.maybeNull(types.reference(User)), // -----
  })                                                        \ 
  // ...                                                     |
                                                             |
export const User = types                                    |
  .model('User', {                                          /
    userId: types.identifier,                      // <<<--
    userName: '',
    firstName: '',
    lastName: '',
    email: '',
    lastVisited: CustomDate,
  })
  // ...

API example

GET https://service.com/api/users

[
  {
    "userId": "g-gqpB1EbTi9XGZOf3IkBpxSjpAHM8fpkeL4YZslEL8",
    "userName": "[email protected]",
    "firstName": "John",
    "lastName": "Doe",
    "email": "[email protected]",
    "lastVisited": "2018-01-18T17:32:48.947"
  },
  {
    ...
  },
  ...
]

GET https://service.com/api/workmanagement/projects?minStartDate=&maxStartDate=2018-11-24&status=Active

[
  {
    "projectId": 1,
    "title": "Project Title",
    "description": "Praesent luctus vitae nunc sed condimentum. Duis maximus arcu tortor, vitae placerat enim ultrices sed. Etiam scelerisque gravida justo, vitae venenatis dui pellentesque eu.",
    "projectStatus": "active",
    "startDate": "2018-08-20T00:00:00",
    "endDate": "2019-07-01T00:00:00",
    "owner": "g-gqpB1EbTi9XGZOf3IkBpxSjpAHM8fpkeL4YZslEL8",
  },
  {
    // ...
  },
  ...
]
like image 879
Igor Soloydenko Avatar asked Nov 25 '18 03:11

Igor Soloydenko


2 Answers

One solution you could apply is in the ProjectModel model to define the owner as a string

owner: types.maybeNull(types.string), 

Then define a computed property on the model that will retrieve the owner as an object

export const ProjectModel = types
  .model('Project', {
    projectId: types.optional(types.identifierNumber, 0),
    title: '',
    descriptionStatus: '',
    projectStatus: '',
    startDate: CustomDate,
    endDate: CustomDate,
    owner: types.maybeNull(types.reference(User)), // -----
  }).views(self => ({
  get ownerObject(): undefined | Instance<typeof User> {
    // get the user manually from the tree
    const root = getRoot(self)

    return resolveIdentifier(User.Type, root, self.owner)
  }
}))

You could use it like this project1.ownerObject ? project1.ownerObject.firstName : '' The downside is that you will always need to check the ownerObject exists whenever you are using it.

like image 200
etudor Avatar answered Nov 06 '22 12:11

etudor


It sounds like you're potentially loading the user data after the project model is created It might be as easy as using a late reference. This is meant for, as it sounds, any types that may get created after this model is created.

instead of this owner: types.maybeNull(types.reference(User))

try this owner: types.maybeNull(types.late(() => types.reference(User)))

like image 27
Harvolev Avatar answered Nov 06 '22 11:11

Harvolev