mst-gql
mst-gql copied to clipboard
Dealing with incomplete data - merging new data to a model
Hi I'm trying to use mst-gql - I've got some really large models that are rarely used in full.
They've scaffolded with nullable types so that partial data requests can be made as explained here:
Dealing with incomplete objects
GraphQL makes it possible to query a subset of the fields of any object. The upside of this is that data traffic can be minimized. The downside is that it cannot be guaranteed that any object is loaded in its 'complete' state. It means that fields might be missing in the client state, even though are defined as being mandatory in the original graphQL object type!...
When I make a query for partial data and commit it to the model I expected it would merge with the existing data on the model but instead the previous data gets deleted
Let's say we have a model like this:
{
firstName: 'Mister',
lastName: 'Freeze',
nested: { isFavorite: false },
}
How to merge additional data coming in like
{
nested: {
age: 40,
gender: 'male',
}
}
So far I just use object spread to merge the additional data which results in the nested
key getting overwritten
Is it even required to merge the new data - it seems to already be converted to a model from the query result but I expected it would somehow get merged with any previously fetched data for that model
Here's my actual model code:
import {applySnapshot, getSnapshot, Instance} from "mobx-state-tree"
import {PatientTypeModelBase, patientTypeModelPrimitives, selectFromPatientType} from "./PatientTypeModel.base"
/* The TypeScript type of an instance of PatientTypeModel */
export interface PatientTypeModelType extends Instance<typeof PatientTypeModel.Type> {}
/* A graphql query fragment builders for PatientTypeModel */
export {selectFromPatientType, patientTypeModelPrimitives, PatientTypeModelSelector} from "./PatientTypeModel.base"
/**
* PatientTypeModel
*
* Patient data
*/
export const PatientTypeModel = PatientTypeModelBase
.actions(self => ({
/**
* Prepare initial data for the dashboard
*/
fetchInitialData() {
const { clientId, store } = self;
const query = store.queryPatient({ clientId }, PROFILE_DATA_FRAGMENT);
// the patient param here seems to already be converted to a reactive object
// but it does not contain the previously fetched info - just the one from the current query
query.then(({ patient }) => {
// First I tried Object.assign(self, patient);
// this results in problems because mst thinks we're resetting the identifier fields
// Then I tried doing nothing at all - maybe the query code and the type system
// already handles it automatically and I have some other problem
// Doing the bellow works but the previous data gets deleted not merged
// const snapshot = getSnapshot(patient);
// applySnapshot(self, snapshot);
console.log('JSON.stringify(self): ', JSON.stringify(self));
})
return query;
}
}))
.views(self => ({
get statusPriority() {
return self.status?.priority || Infinity;
},
}))
const PROFILE_DATA_FRAGMENT = selectFromPatientType()
.client_id
.first_name
.last_name
.gender
.emr_data
.dob
.client(c => c
.client_id
.email
.phone
.created)
.image(i => i.large)
.toString();
I regularly use mobx and mobx-react, but have no actual experience with mobx-state-tree so I maybe missing something on that end
MST should automatically merge the data for you. Note that if you look at the data on the query you might still only be seeing the specific fields asked for as part of PROFILE_DATA_FRAGMENT
. You'll need to look in your store to see the full merged object.
What type is nested
in this case? Is this a nested model? a map?
What type is nested in this case? Is this a nested model? a map? It's a nested model from the scaffold
I noticed something else, that I think was the issue: The root types were not picked correctly. Now that I added a configuration with explicit array of root types - collections (maps) were correctly generated across the root models, but I'm dealing with another issue now - an exception when a query is fetched because it cannot be serialized to the model for some reason - I think it's because it holds an array of objects with no id
identifier - it's just a wrapper for 2 other object that do have ids... I'm almost certain the issue is with our gql schema having though
I also seem to have other problems related to circular deps
I'll try to confirm whether the root models fixed this
I've created another root store model to separate some of the logic to a different store - this might be the cause too - having all the methods and props in the RootStore will just make it way too big for our app, while the sub models are mostly infrastructure and cannot serve to hold large portions of logic there
@kidroca
could your problems be related to the fact that mst-gql seems to expects a single id field to exist and be named id
? https://github.com/mobxjs/mst-gql/issues/223, https://github.com/mobxjs/mst-gql/issues/215
Just ran into the same issue here. It seems that merging doesn't happen properly for objects without an ID and instead it just replaces the whole object. I'll see if I can make a patch for this
I had to solve the same problem in mst-query (which originally had the same merge logic as this library).
Maybe some parts of my solution can be helpful: https://github.com/ConrabOpto/mst-query/blob/61865be905cbf5cd98690155c5317636571f9784/src/merge.ts#L68
I had to solve the same problem in mst-query (which originally had the same merge logic as this library).
Maybe some parts of my solution can be helpful: https://github.com/ConrabOpto/mst-query/blob/master/srcmerge.ts#L59
Hey @k-ode It seems the link is broken. Can you kindly update it? TIA.
@venkateshpullaganti Fixed it
Will take a look at this soon, thank you!