-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Create ZQuery
package for managing data in Zustand
#1199
Conversation
🦋 Changeset detectedLatest commit: e7b4a41 The changes in this PR will be included in the next version bump. This PR includes changesets to release 14 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
da2dc89
to
86d9ddb
Compare
useZQuery
hook for managing dataZQuery
package for managing data in Zustand
ZQuery
package for managing data in ZustandZQuery
package for managing data in Zustand
4531b14
to
35e6633
Compare
ZQuery
package for managing data in ZustandZQuery
package for managing data in Zustand
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Impressive work!
Overall, ZQuery feels like a complicated multi-layer abstraction but the one I could personally get used to. I left some comments in form on the conventional comments. None of them actually prompt you to change anything but we could have a discussions in the comment threads to clarify some points
}>; | ||
} | ||
|
||
export const createStatusSlice = (): SliceCreator<StatusSlice> => () => ({ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
thought: As a user, I'd expect to use the state inside Zustand with selectors as we always do, so I'd like for ZQuery to just return a slice that I can simply plug into the store and play with it. I don't think the return from ZQuery { status, useStatus }
is really relevant. Can we make ZQuery just a kinda create-and-forget type, so all the interactions with it would be through Zustand?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That was actually my initial design for it, but one of the goals of this project was to integrate into the React lifecycle hooks so that data would get fetched on component mount, and any in-flight requests would be aborted on component unmount. Using just selectors means that we would miss out on that benefit.
I do understand the desire for something that "feels" more like Zustand, though. If we decide to merge this PR as-is, we could still revisit that question in the future.
fullSyncHeight: item.fullSyncHeight, | ||
latestKnownBlockHeight: item.latestKnownBlockHeight, | ||
}), | ||
getUseStore: () => useStore, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: why do we actually need the whole store for manipulating with just one slice? Aren't getters and setters enough?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ugh, unfortunately we need it for actually calling the hook (e.g., here). I wanted to avoid this but, since we're actually hooking into the React component lifecycle, we need to actually use the useStore
hook (rather than just the getter/setter).
set: setter => { | ||
const newState = setter(useStore.getState().status.status); | ||
useStore.setState(state => { | ||
state.status.status = newState; | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
question: from the DX perspective, can setter
accept a newState
argument with the getter()
return, so that we won't have to query it every time?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good call. I'd originally used the design you described, then had to switch to this design for some TypeScript reason. I think that reason has since become moot, so I should be able to switch it back. Will look into that and follow up. Thanks for catching that! (This DX is indeed quite confusing.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, wasn't able to resolve this. For other reviewers' benefit, I've messaged Max separately about pairing on resolving the TS issues related to this; until then, though, I'd still love a PR review on this!
1d94970
to
e619244
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Holy smokes, this is a full blown professional library addon. Think we should pitch Zustand-core for an integration 😅
Think it's definitely in a place where we can feel confident about merging and brainstorming (in a more incremental way) updates, new features, api changes, etc.
Cool stuff!
const inputExponent = getDisplayDenomExponent.optional()(inputMetadata); | ||
const outputExponent = getDisplayDenomExponent.optional()(outputMetadata); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bug fix?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yep — weirdly, some asset metadata was missing in testnet that I'd had in my wallet.
e619244
to
e7b4a41
Compare
ZQuery
is a new package for managing remote data in Zustand.Navigating this PR
In this PR, I've created the ZQuery package. The bulk of its code is here, and its types are here.
This PR is a bit large because I've also implemented its use in a few places throughout the app to test it:
<SyncStatusSection />
component<Status />
component that was being used to sync status data to Zustand, since this is now handled automatically by hooks<AuctionList />
to useuseStatus()
rather than getting the status from the store<TokenSwapInput />
to useuseStatus()
rather than getting the status from the storeunclaimedSwaps
Zustand slice<UnclaimedSwaps />
componentdutchAuction
Zustand slice to use ZQuery for theauctionInfos
array.<AuctionList />
component to use the newuseAuctionInfos()
hookMain missing features for future improvements
The biggest one: we don't currently cache based on parameters. Ideally, we should be able to do something like this:
const auctionInfos = useAuctionInfos({ cacheKey: 'latestState', fetchArgs: [{queryLatestState: true }] });
. Then, the results of calling theauctionInfos
fetcher with{queryLatestState:true}
would be cached under thelatestState
cache key. If you just callconst auctionInfos = useAuctionInfos()
— i.e., without any arguments — the results would be stored under e.g., a_zQueryDefaultCacheKey
cache key, and would be returned any time a consumer callsuseAuctionInfos()
without any arguments.This is actually a bit complex to implement, though, and this PR is a bit large already, so I figured it's best to ship this as-is and revisit this later if needed.
We're not using a finite state machine yet. That is, it's possible for
error
anddata
andloading
to all be truthy at the same time. In the interest of limiting this PR to a reasonable scope, I'm pushing that off till later.We don't truly abort calls when a new request is started. We call an abort controller's
abort()
method, but all that does is stop iterating over the results. I'm not familiar enough with generator functions to know if that actually stops the server from streaming data, but I'm pretty sure it doesn't? Could be wrong, though. In the future, we'll likely want to pass abort controllers (or at least theirsignal
property) to thefetch
callback so that we can abort remote calls properly.