Skip to content
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

Merged
merged 68 commits into from
Jun 12, 2024

Conversation

jessepinho
Copy link
Contributor

@jessepinho jessepinho commented May 29, 2024

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:

Main 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 the auctionInfos fetcher with {queryLatestState:true} would be cached under the latestState cache key. If you just call const 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 calls useAuctionInfos() 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 and data and loading 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 their signal property) to the fetch callback so that we can abort remote calls properly.

Copy link

changeset-bot bot commented May 29, 2024

🦋 Changeset detected

Latest commit: e7b4a41

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 14 packages
Name Type
@penumbra-zone/zquery Major
@penumbra-zone/getters Minor
minifront Patch
@penumbra-zone/ui Patch
@penumbra-zone/perspective Patch
@penumbra-zone/query Patch
@penumbra-zone/services Patch
@penumbra-zone/storage Patch
@penumbra-zone/types Patch
chrome-extension Patch
node-status Patch
@penumbra-zone/services-context Patch
@penumbra-zone/crypto-web Patch
@penumbra-zone/wasm Patch

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

@jessepinho jessepinho force-pushed the jessepinho/z-query-web-965 branch 11 times, most recently from da2dc89 to 86d9ddb Compare June 6, 2024 05:18
@jessepinho jessepinho changed the title WIP: useZQuery hook for managing data Create ZQuery package for managing data in Zustand Jun 6, 2024
@jessepinho jessepinho changed the title Create ZQuery package for managing data in Zustand WIP: Create ZQuery package for managing data in Zustand Jun 6, 2024
@jessepinho jessepinho force-pushed the jessepinho/z-query-web-965 branch 2 times, most recently from 4531b14 to 35e6633 Compare June 10, 2024 17:45
@jessepinho jessepinho changed the title WIP: Create ZQuery package for managing data in Zustand Create ZQuery package for managing data in Zustand Jun 10, 2024
@jessepinho jessepinho marked this pull request as ready for review June 10, 2024 22:07
@jessepinho jessepinho requested a review from a team June 10, 2024 22:07
Copy link
Contributor

@VanishMax VanishMax left a 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> => () => ({
Copy link
Contributor

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?

Copy link
Contributor Author

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,
Copy link
Contributor

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?

Copy link
Contributor Author

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).

Comment on lines +20 to +24
set: setter => {
const newState = setter(useStore.getState().status.status);
useStore.setState(state => {
state.status.status = newState;
});
Copy link
Contributor

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?

Copy link
Contributor Author

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.)

Copy link
Contributor Author

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!

@VanishMax VanishMax requested a review from a team June 11, 2024 10:27
@jessepinho jessepinho force-pushed the jessepinho/z-query-web-965 branch from 1d94970 to e619244 Compare June 11, 2024 17:43
Copy link
Contributor

@grod220 grod220 left a 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!

Comment on lines +42 to +43
const inputExponent = getDisplayDenomExponent.optional()(inputMetadata);
const outputExponent = getDisplayDenomExponent.optional()(outputMetadata);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug fix?

Copy link
Contributor Author

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.

@jessepinho jessepinho force-pushed the jessepinho/z-query-web-965 branch from e619244 to e7b4a41 Compare June 12, 2024 19:55
@jessepinho jessepinho merged commit 6b06e04 into main Jun 12, 2024
6 checks passed
@jessepinho jessepinho deleted the jessepinho/z-query-web-965 branch June 12, 2024 20:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants