How to structure stores for both paginated and unpaginated data #2130
-
I have this model that is used to store information about field groups. Now, there is another feature that I need to add which is of reordering the field groups. My question is what would be the right way to store both the paginated data and the full data. As much as I love mobx-state-tree, the problem that I have been facing ever since using it is that there are not much resources that talk about how to structure the models and stores. export const ContactFieldGroupModel = types
.model({
id: types.identifierNumber,
name: types.string,
description: types.maybeNull(types.string),
color: types.maybeNull(types.string),
icon: types.maybeNull(types.string),
created_by: types.maybeNull(UserModel),
})
export const ContactFieldGroupStoreModel = types
.model({
fieldGroups: types.array(ContactFieldGroupModel),
count: types.optional(types.number, 0),
page: types.optional(types.number, 0),
hasMore: types.optional(types.boolean, false),
loading: types.optional(types.boolean, false),
})
.actions((self) => ({
getFieldGroups: flow(function* (page: number, limit?: number) {
console.log('getFieldGroups');
const resp = yield* toGenerator(api.getContactFieldGroups({ page, limit }));
if (resp.kind !== 'ok') {
self.loading = false;
return;
}
self.fieldGroups = resp.fieldGroups;
self.page = page;
self.count = resp.count;
self.hasMore = resp.hasMore;
self.loading = false;
}),
async refreshFieldGroups() {
self.loading = true;
await this.getFieldGroups(1);
},
async getMoreFieldGroups() {
self.loading = true;
await this.getFieldGroups(self.page + 1);
},
})) |
Beta Was this translation helpful? Give feedback.
Replies: 4 comments 5 replies
-
Hey @Dummy26 - thanks for your question! I don't think there's one "right" way to do this, Since you only care about a subset of fields for reordering, you might be able to load that data without pagination, assuming you can query your database and return just the id and name fields. I would personally load that up into a separate store or model for your re-ordering feature, and then treat it as de-normalized data. Once you load that all up into the reordering feature, you can reorder the fields, write the new order of them into whatever persistent database you have, and make sure the I agree there aren't enough resources out there on MST patterns. I've been trying to blog about MST as much as I personally have time for, but we can always use more people chiming in. I would encourage you to evaluate the tradeoffs of my recommendation and write about it yourself to help others in the future. Perhaps there's a good reason for you to paginate through the re-ordering data, or maybe if you don't have too many In my day-job, we tend to oscillate between data loading strategies based on our priorities at any given time, and based on changing feature requirements. It's tough to find an approach that works across the board. Usually we pick whatever seems easiest and fastest to implement, and only change it if we run into a clearly identifiable problem. |
Beta Was this translation helpful? Give feedback.
-
I created another store for the re-order feature const FieldGroupModel = types.model({
id: types.identifierNumber,
name: types.string,
});
const ContactConfigStoreModel = types
.model({
fieldGroups: types.array(FieldGroupModel),
count: types.optional(types.number, 0),
loading: types.optional(types.boolean, false),
})
.actions((self) => ({
setFieldGroups: (fieldGroups) => {
self.fieldGroups = fieldGroups;
},
getAllFieldGroups: flow(function* () {
const resp = yield* toGenerator(api.getContactFieldGroups({ page: 1, limit: 1000 }));
if (resp.kind !== 'ok') {
self.loading = false;
return;
}
self.fieldGroups = resp.fieldGroups;
self.count = resp.count;
self.loading = false;
}),
reorderFieldGroups: flow(function* () {
const newOrder = {};
for (let i = 0; i < self.fieldGroups.length; i += 1) {
const fieldGroup = self.fieldGroups[i];
newOrder[fieldGroup.id] = i;
}
yield* toGenerator(api.reorderContactFieldGroups({ ordering: newOrder }));
}),
}));
Can you explain this more. Also can you shed some light on how to invalidate the old paginated data on re-order. Is there a mobx way to do that or do I need to manually call the refresh function of |
Beta Was this translation helpful? Give feedback.
-
I came across This is how I have used it const ContactFieldGroupStoreModel = types
.model({
pool: types.array(ContactFieldGroupModel),
fieldGroups: types.array(types.reference(ContactFieldGroupModel)),
count: types.optional(types.number, 0),
page: types.optional(types.number, 0),
hasMore: types.optional(types.boolean, false),
loading: types.optional(types.boolean, false),
})
.extend(withReferencePool(ContactFieldGroupModel))
.actions((self) => ({
getFieldGroups: flow(function* (page: number, limit?: number) {
const resp = yield* toGenerator(api.getContactFieldGroups({ page, limit }));
if (resp.kind === 'ok') {
self.fieldGroups = self.addAllToPool(resp.fieldGroups);
self.page = page;
self.count = resp.count;
self.hasMore = resp.hasMore;
}
self.loading = false;
}),
})); const ContactConfigStoreModel = types
.model({
fieldGroups: types.array(types.reference(ContactFieldGroupModel)),
count: types.optional(types.number, 0),
loading: types.optional(types.boolean, false),
})
.actions((self) => ({
getAllFieldGroups: flow(function* () {
const resp = yield* toGenerator(api.getContactFieldGroups({ page: 1, limit: 1000 }));
if (resp.kind === 'ok') {
const fieldGroupPoolStore = getRoot(self).contactFieldGroupStore;
self.fieldGroups = fieldGroupPoolStore.addAllToPool(resp.fieldGroups);
self.count = resp.count;
}
self.loading = false;
}),
setFieldGroups: (fieldGroups) => {
self.fieldGroups = fieldGroups;
},
})); Now, the fieldGroups are stored only in the pool and both the stores contain references to the pool. I have tried using mst-reference-pool in another similar re-ordering feature for fields. |
Beta Was this translation helpful? Give feedback.
-
As another suggestion, my library mst-query const ContactFieldGroupQuery = createQuery('ContactFieldGroupQuery', {
data: types.model({
fieldGroups: types.array(types.reference(ContactFieldGroupModel)),
count: 0,
page: 0,
hasMore: types.boolean,
}),
request: types.model({ order: 'asc' }),
pagination: types.model({ page: types.number, limit: types.number, }),
async endpoint({ request: { order }, pagination: { page, limit } }) {
return api.getContactFieldGroups({ page, limit, order });
},
});
// This store will have a field "models" with all the models that the rest of your tree can reference
const ContactFieldGroupStore = createModelStore('ContactFieldGroupStore', ContactFieldGroupModel).props({
contactFieldGroupQuery: types.optional(ContactFieldGroupQuery, {}),
}).actions(self => ({
afterCreate() {
onQueryMore(self.contactFieldGroupQuery, (data) => {
self.contactFieldGroupQuery.data.fieldGroups.push(...data.fieldGroups);
});
}
});
const ContactFieldGroups = observer((props) => {
const [page, setPage] = useState(0);
const [order, setOrder] = useState('asc');
const { data, isLoading, isFetchingMore } = useQuery(store.contactFieldGroupStore, {
request: { order },
pagination: { page, limit: 50 },
});
if (!data || isFetchingMore) {
return <div>Loading...</div>;
}
return <></>
}); |
Beta Was this translation helpful? Give feedback.
Hey @Dummy26 - thanks for your question! I don't think there's one "right" way to do this,
Since you only care about a subset of fields for reordering, you might be able to load that data without pagination, assuming you can query your database and return just the id and name fields. I would personally load that up into a separate store or model for your re-ordering feature, and then treat it as de-normalized data. Once you load that all up into the reordering feature, you can reorder the fields, write the new order of them into whatever persistent database you have, and make sure the
ContactFieldGroupStoreModel
has a way to invalidate its old pagination data, and re-fetch if appropriate.I…