Composite Key/Identifier - Get #1762
-
Is there a prescribed/best-practice approach to handle identifiers which consist of multiple keys? I've been trying via custom reference implementation: https://mobx-state-tree.js.org/concepts/references I am playing with this todo example and am trying to accomplish the use of a composite key for
Which together equate to the identifier stored in the 'id' field of the user. To have composite id working with Mobx:
All seems to work: /**
* Composite id of a user
*/
interface ICompositeIdUser{
name: string;
uuid: string;
}
/**
* Custom type specifically for holding moments in the MST
*/
const momentDate = types.custom({
name: "momentDate",
fromSnapshot(value: string):Moment {
return moment(value)
},
toSnapshot(value: Moment) {
return value.toISOString()
},
isTargetType(v:Moment) {
return moment.isMoment(v);
},
getValidationMessage(v:string) {
try {
// tslint:disable-next-line: no-unused-expression
moment(v)
return ""
} catch (e) {
return e.message
}
}
})
/**
* Build composite identifier from parts
*
* @param name
* @param uuid
*/
function buildCompositeIdUser(name:string, uuid: string):string {
return name+"::"+uuid;
}
/**
* Retreive composite id parts from identifier
*
* @param identifier
*/
function retrieveCompositeIdUser(identifier:string): ICompositeIdUser {
let parts:string[] = identifier.split("::");
return {name:parts[0], uuid:parts[1]};
}
/**
* User responsible for tasks
*/
const User = types.model({
name: types.string,
uuid: types.string,
// make id type optional so that can pass in model without it when passing to action, but then
// have action fill id in
// @ts-ignore
id: types.optional(types.identifier, ()=>{ throw Error('id not defined.'); return ""}),
})
const UserCompositeId =
types.safeReference(User, {
// given an identifier, find the user
get(identifier:string, parent: any /*Store*/) {
let identifierParts:ICompositeIdUser = retrieveCompositeIdUser(identifier);
console.log('identifier', identifier, identifierParts.name, identifierParts.uuid);
if (typeof identifier != 'undefined') {
const root = getRoot(parent)
console.log('root', root);
// @ts-ignore
return root.users.get(identifier) || null
}
},
// given a user, produce the identifier that should be stored
set(value /* User */) {
return buildCompositeIdUser(value.name,value.uuid);
}
}
)
/**
* Todo that must be done
*/
const Todo = types.model({
text: types.string,
createdTs: momentDate,
id: types.identifier,
// define user responsible as a reference, with late resolution so as to avoid errors
userResponsible: types.maybe(UserCompositeId)
})
/**
* Root of the MST, stored here.
*/
const Store = types.model({
todos: types.map(Todo),
users: types.map(User)
}).actions((self) => {
return {
// The typeof operator below is the important one: this is how you interact with types introduced
// by mobx-state-tree
addTodo (todo: typeof Todo.Type) {
if (todo.userResponsible) {
let root = getRoot(self);
// @ts-ignore
root.addUser(todo.userResponsible);
// @ts-ignore
todo.userResponsible = buildCompositeIdUser(todo.userResponsible.name, todo.userResponsible.uuid);
}
// using maps, and overwriting ids
self.todos.put(todo);
},
removeTodo(todo: typeof Todo.Type) {
// using maps and deleting based on id
self.todos.delete(todo.id.toString());
},
addUser(user: typeof User.Type) {
// augment user with id automatically with a composite id so the built in mechanism
// for ids actually works
user.id = `${buildCompositeIdUser(user.name, user.uuid)}`;
self.users.put(user);
},
removeUser(user: typeof User.Type) {
// using maps and deleting based on id
self.users.delete(user.id!.toString());
}
}
}); Is this an appropriate approach? Is there a better way? I've noticed that |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 1 reply
-
To be honest, my approach would probably be a lot less fancy. I'd just preprocess the snapshot import { types } from "mobx-state-tree";
const User = types
.model({
name: types.string,
uuid: types.string,
id: types.optional(types.identifier, "")
})
.preProcessSnapshot((snapshot) => {
const id = `${snapshot.name}::${snapshot.uuid}`;
return { ...snapshot, id };
});
const UserStore = types.model({
users: types.array(User),
currentUser: types.reference(User)
});
const myUser = User.create({ name: "Jamon", uuid: "sadf" });
const userStore = UserStore.create({
users: [myUser],
currentUser: myUser.id
}); Sandbox here: https://codesandbox.io/s/angry-kepler-fukf6?file=/src/index.ts |
Beta Was this translation helpful? Give feedback.
To be honest, my approach would probably be a lot less fancy. I'd just preprocess the snapshot
Sandbox here: https://codesandbox.io/s/angry-kepler-fu…