mobx-state-tree
mobx-state-tree copied to clipboard
Cannot resolve a reference with multiple candidates (two model instances with same identifier)
Bug report
- [x] I've checked documentation and searched for existing issues
- [x] I've made sure my project is based on the latest MST version
- [ ] Fork this code sandbox or another minimal reproduction.
Sandbox link or minimal reproduction code Sandbox example
Describe the expected behavior
I have a Base
model that is composed in two places one, two
in the store tree. This model uses nested models Country
and City
. I add a new country and a new city in both places with the same identifiers. The cities are also placed in an array of references at the country model. Now I want to get the cities of a country by reading the list of cities references.
Describe the observed behavior
When trying to get the list of cities from references list of Country
model, the following error throws:
[mobx-state-tree] Cannot resolve a reference to type 'late(function () {
return City;
})' with id: '1000' unambigously, there are multiple candidates: /one/cities/1000, /two/cities/1000
The same is NOT happening when trying to get a country, even though there are multiple countries with the same identifier, but not in a list of references.
It seams that resolving an item by its reference identifier not working if there are multiple items with the same identifier.
https://github.com/mobxjs/mobx-state-tree/blob/master/README.md#references
Identifiers for objects of the same kind must be unique across the whole tree that spawns the whole root store, this is because in their snapshot form references are saved as plain strings /numbers that must point to a single object.
There are ways to escape this such as custom references or views that resolve the nodes in a custom way.
In case you don't need to use references then you don't need to use identifiers and can just use a simple string or number.
Not sure if that's what you meant though 🙂
Thanks for the answer.
I understand what you are saying but there might be situations where a model needs to be used in multiple places in the store. For example, imagine a User
model that can be used for regular product users and for administrator users, both stored at different arrays. The only solution now is to duplicate the model.
Anyway, at the time, I solved my problem by replacing the safeReference
type property with a view getter that returns the same thing.
Another way to tackle the problme would be to use a custom reference that resolves to a certain array in the store rather than relying on the built-in resolveIdentifier.
To be honest I don't use references that much, I usually just use ids as properties and views to resolve them :)
Btw, if you have any suggestion on how MST should tackle the problem instead of the current implementation feel free to do so!
This is a useful thread. What I am confused about is this: What is MST's recommended workflow for deep cloning a tree that uses reference
or safeReference
?
In my case I have a complex structure where a parent references a child and that child references another child, and each contains references to objects below in the tree....... I need to be able to clone an entire partial tree, to make copies of a model instance and all of its children.
Seems like I have two options:
- give up on
reference
andsafeReference
, like @zisiszikos did above OR - When-ever I clone part of a tree, I need to take a snapshot of the part of tree I want to clone, and iterate over all the children in this tree, setting up "new" children each time, copying all the data into them but creating new unique ids for each one?
It sems like this is not necessarily a simple issue to solve. For example, deep cloning model "associations" is not built into Ruby On Rails, to do that I think you need to write your own code or use a gem like this:
https://github.com/moiristo/deep_cloneable
This issue has been automatically marked as stale because it has not had recent activity in the last 10 days. It will be closed in 4 days if no further activity occurs. Thank you for your contributions.
I agree, this is an important issue. Having the application break when you try to use a model in 2 locations is really unhelpful... Especially because there is no way to add a 'generic' model property to a model, so you can abstract some logic, and A node cannot exists twice in the state tree
. It's not an uncommon thing for a record to appear in an application twice, right?
What would be the recommended way to reference generic models? The use case I have is that a user can select elements from different pages to add them to a map, so I'd need an array of references to models, and not know exactly how they fit in the tree... Is there a way to do this?
I'm having a similar issue (same User appearing in various arrays) except I'm not using references, but just resolveIdentifier
. However, I just need to resolve it to get the user name and I don't care which instance (i.e. from which array) I get it.
And I was wondering, would it be possible to include a resolveIdentifierAll
(or some other name) function or add an optional parameter that wouldn't throw an error if there are multiple instances with same id. For my case it would be fine if the function worked as find
, i.e. returning the first.
It's not an uncommon thing for a record to appear in an application twice, right?
It is; if an element appears twice, it means that you didn't create a tree which all other utilities assume (not having a tree for examples makes it impossible to serialize reliably, or to determine unambigously who is the parent of a node). If you find yourself needing to have the same node multiple times in the tree, probably all instances of that except one should be references.
For further questions, please open a new issue, as this one is closed already and others won't see the question.
I wouldn't mind a more elegant solution to this issue, as it's something we've run into ourselves. Re-opening for future consideration.
@jamonholmgren were you able to find a solution?
Maybe use types.string instead of types.identifier helps?
@Navipro70 will that still work with references?
@10000multiplier I don't know, try to test it)
@10000multiplier and @Navipro70 no it won't that's the whole point of identifier.
I've released a new library to help with this problem, which I think is pretty cool, well-tested, and pretty well documented.
Please check it out and let me know what you think about it!
It's called mst-reference-pool and you can find it here:
https://github.com/infinitered/mst-reference-pool
In a nutshell, it gives you tools to easily create a pool of instances, and then you simply use references from anywhere on the tree. It also comes with a garbage collector so you don't end up with a memory leak when instances are no longer referenced from anywhere.
Hey @jamonholmgren, et. al., looks like mst-reference-pool
is a good add-on fix here. I'm going to convert this issue to a discussion because I think there's some opportunity for us to consider how that approach might make sense for MST (I also need this in my own application), but the "issue" seems to be resolved for sure.
Thanks to everyone who chimed in!