Closed k-ode closed 1 year ago
Draft pr here: https://github.com/ConrabOpto/mst-query/pull/33
Now available on npm as 2.0.0-alpha.1
.
Remaining work:
The two last items might happen in a minor release following v2. But I do want to deprecate useLazyQuery and replace with an enabled-prop bere doing a proper release.
React 18 does seem to require a re-think of the api somewhat. A mutable object cannot reliably be stored in a useState hook, it needs to be external to react.
My current thinking is that we double down on the "queries as properties" api, which will solve the React 18 issues AND make mst-query work better with non react setups. The drawback is that you will have to manually garbage collect queries (normalized models will still be gc'ed automatically).
Here's how I think this will look:
const DocumentQuery = createQuery('DocumentQuery', {
data: DocumentModel,
request: types.model({ id: types.string }),
});
const AddCommentMutation = createMutation('AddCommentMutation', {
data: CommentModel,
request: types.model({ text: types.string }),
});
const CommentListQuery = createMutation('CommentListQuery', {
data: CommentList,
request: types.model({ id: types.string }),
pagination: types.model({ offset: 0 }),
});
// Store api
const DocumentStore = types
.model({
documentQuery: DocumentQuery,
addCommentMutation: AddCommentMutation,
commentListQuery: CommentListQuery,
})
.actions((self) => ({
getDocument: flow(function* ({ id }) {
const next = self.documentQuery.query({ request: { id } });
next();
}),
getComments: flow(function* ({ id }) {
const next = self.commentListQuery.query({ request: { id } });
next();
}),
getMoreComments: flow(function* (offset) {
const next = self.commentListQuery.queryMore({
request: { id },
pagination: { offset },
});
const { data } = next();
self.commentListQuery.data.items.push(...data.items);
}),
addComment: flow(function* (text) {
const next = self.addCommentMutation.mutate({ request: { text } });
const { data } = next();
// add directly to local query
self.commentListQuery.data.item.push(data);
// or use query store to add to all matching queries
const { queryStore } = getQueryClient(self);
const commentList = queryStore.findAll(CommentListQuery);
commentList.forEach(list => list.data?.items.push(data));
}),
}));
// Hook api
const DocumentStore = types.model({
documentQuery: DocumentQuery,
documentMutation: DocumentMutation,
commentListQuery: CommentListQuery,
});
const store = DocumentStore.create();
const CommentList = observer(({ id }) => {
const { data } = useQuery(store.commentListQuery, store.getComments, {
request: { id },
onQueryMore: () => store.getMoreComments(offset)
});
const [addComment] = useMutation(store.documentMutation, store.addComment);
const handleAddComment = () => {
addComment({ text: 'new text' });
};
if (!data) {
return <div>Loading comments...</div>;
}
return (
<div>
<div>
{data.items.map((comment) => (
<Comment comment={comment} />
))}
</div>
<button onClick={handleAddComment}>Add comment</button>
</div>
);
});
const Document = observer(({ id }) => {
const store = useStore();
const { data } = useQuery(store.documentQuery, store.getDocument, {
request: { id }
});
if (!data) {
return <div>Loading document...</div>;
}
return <CommentList id={id} />;
});
Beta version now available as 2.0.0-beta.1
. This release is more or less complete, there's some minor adjustments and docs work left.
A demo working with React 18 and Strict Mode: https://codesandbox.io/s/mst-query-table-filters-wdforg
v2 is now released
For the next version of mst-query, we're planning some breaking changes to the api. After using mst-query in production and learning things about it for more than a year now, there are things that we'd like to improve.
The following list is some of the changes we're actively considering.
Using queries and mutations as props on models
This is part of a goal to make mst-query more usable for classical mobx-state-tree applications, and in the long term also be able to support other rendering libraries other than React.
Since this is not a breaking change, this is already possible today in 1.1.0-alpha releases.
Change RequestModel
In hindsight, the current iteration of the request model seems like a mistake. While convenient in some cases, it's also very easy to end up with stale request data, and keeping component props and request state in sync tends to be very error prone.
Instead we'd like to encourage a pattern of sending data directly to
run
actions.We do find validating request arguments useful, and the additional type safety it brings when using
queryClient.find(q => q.request === ...)
. Therefore we're considering keeping it around as an optional way to get more type safety in your queries. In this version the request model will get updated every time you pass new data toself.query
orself.mutate
.Remove env
This api is not important enough that it needs to be a first class api in mst-query. We don't do anything special with it and it's easy to add it back if you still want it.
Change how request works in react hooks
Most developers intution seems to be that whenever arguments in request changes, the query will re-run with the new arguments. This is how it works in
react-query
and similar libraries, but not inmst-query
. The plan is to align the api and behaviour of mst-query to that of other query libraries.The new request argument will automatically pass its contents to run of the query. Request will be removed from
useMutation
, as you’ll pass data to run directly.Deprecate useLazyQuery in favor of an enabled prop
An
enabled
prop is a flexible solution to support many different patterns. A query will not run if enabled is false, and will defer running until it is true.Other things we’re looking at
React 18 & Suspense
We definitly want mst-query to work with React 18 and Suspense. It’s under investigation, but at this time we have no reason to believe that this will lead to any further breaking changes.
Invalidating & Prefetching
We want to add first class apis for invalidating and prefetching queries. The api will problaby be similar to react-query.