References seem to be a really nice way to define relationships between models. However, I cannot find any explanation or pattern for managing bidirectional references.
For example, imagine a User, Group and Membership model where Membership has references to a User and a Group. A typical use case would involve showing all the memberships of a particular user, or a particular group. All three models would be stored in a map inside an overall Store.
How can we define such situation in MST? Would it be useful to add an example to the docs (preferably using TypeScript)?
I've tried getParent() to link to a root store and then apply filtering (e.g. on User, define a view "get memberships" that does getParent, then goes to all memberships, then filters on member.user == self . However, already by using getParent we loose type checking as the parent type cannot be resolved at compile time.
Thanks for the help, keep up the good work!!
I have several ideas how to do it, but all of them involving using getParent
First you can use getParent in a kinda "safe" way
interface IParentInterface {
associations: Array<{userId: string; membershipId: string; groupId: string; }>;
}
.views(self => {
return {
get _getParent(): IParentInterface {
// make some runtime validations if you feel the need
return getParent(1 /* or more */, self);
}
};
}).views(self => {
return {
get associations() {
// make some runtime validations
if (!self._getParent.associations) {
throw new Error("oopppss. where's your daddy?");
}
return self._getParent.associations;
},
}
});
But we still want to avoid direct circular refs, so we just keep the associations as ids,
And lets say you are on the Group model and you want to get all your users:
.views(self => {
return {
get users(): Array<typeof UserModel.Type> {
return self.associations.filter(a => a.groupId === self.id).map(a => resolveIdentifier(UserModel, getRoot(self), a.userId));
},
}
});
BTW on mobx, i also using tree-shaped models, and i'm using explicit dependency injections.
With mobx i would inject the associations array and users array, or "usersRegistryService"
on MST you don't need explicit dependency injections because you can traverse up the tree
this is possible using normal references and a late type.
within UserModel:
types.reference(types.late(() => Group))
within GroupModel:
types.reference(types.late(() => User))
@robinfehr any way to make that work with typescript?
The any's seem to kill the types for the IDE.
@ConneXNL this might help you - #417
@robinfehr The example you provided doesn't work.
The reference from Group back to user is undefined.
It looks like the model is incorrect, because it is GroupModel.group , whereas it should be .user.
Changing it to user throws an error.
Most helpful comment
I have several ideas how to do it, but all of them involving using getParent
First you can use
getParentin a kinda "safe" wayBut we still want to avoid direct circular refs, so we just keep the associations as ids,
And lets say you are on the Group model and you want to get all your users: