-
Notifications
You must be signed in to change notification settings - Fork 2
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
Profile server client refresh #420
base: dev
Are you sure you want to change the base?
Conversation
Refactors and optimises the data fetching of the tabs pages to speed up the loading of the data
The latest updates on your projects. Learn more about Vercel for Git ↗︎
|
typo in filename, console logs, redundant business logic
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.
I'm struggling a bit to come to grips with how all of this works. I understand we're polling from the client side now, but I'm not sure how the api routes get invalidated as they will be caching results as well. Calling revalidateServerPath("profile/[address]")
will invalidate the cached data for all server components. I take it that it will invalidate all api's under that "profile/address" path as well? Because that data is now not server-component bound anymore but being fetched using react-query.
Another question: how does this data get invalidated if the user closes their window before the transaction completes? Are we then stuck with stale data until the next invalidation due to interaction (or vercel deploy)?
return { ...fraction, metadata: metadata?.data ?? null }; | ||
}), | ||
); | ||
claimable.data = processedData; |
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.
Reassigning this is a bit unidiomatic.
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.
what do you mean? should it be named differently? Suggestions?
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.
I mean doing something more like { ...claimedable, data: processedData }
, so we avoid mutating values. I think in this case the chances of errors popping up due to this reassignment are minimal tho, as it's a lambda function that doesn't share references with anything else. To be fair, it's a bit of a nitpick but I usually try to avoid doing this.
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.
Are you ok with it being a temporary implementation untill we have hypercerts-org/hypercerts-api#240
import { useQuery } from "@tanstack/react-query"; | ||
|
||
export const ClaimableContent = ({ address }: { address: string }) => { | ||
const { data: response, isLoading } = useQuery({ |
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.
I would prefer to see this split out to a separate file, so that revalidation issues are fixed everywhere automatically once this gets used in a different plaec as well. That would also allow for specifying the return type in the hook instead of having to cast the data type after.
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.
The typecasting is only temporary untill we have this fixed in the API. Basically you can't get allow list records with metadata at the moment, hence the attaching of metadata in the API endpoint as well.
The query key is specific to this file, so why would we split out the hook that's only used in one place?
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.
It's currently also used to get the tab counts? Granted, due to the query key being equal the call will only be done once, and both these components will just pull the results from the query cache.
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.
moved into hooks and pushed
}); | ||
|
||
const { data: owned } = useQuery({ | ||
queryKey: ["hypercerts-owned", address.toLowerCase()], |
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.
This is a good example of where we would benefit from splitting out those hooks to their own files. The key differs between this one and the one used in hypercerts-tab-content-owned
("hypercerts-owned"
vs "owned-hypercerts"
) which means that
- invalidating won't updated the hypercerts everywhere
- we're fetching twice (and polling) for the same exact data instead of reusing
- more surface area for typing errors
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 catch, but this call is actually painfully inefficient for only getting the call. Wouldn't it be better to just call for the count?
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.
Doing a call just for the count would also work.
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.
Using the same hooks as for the content components untill this is resolved: hypercerts-org/hypercerts-api#255
Added a TODO. It would be a nice to have, so we can progressively render the tab content, skeletons and actual data
queryKey: ["hypercerts-data", address.toLowerCase()], | ||
}); | ||
await revalidatePathServerAction(`/profile/${address}`); | ||
router.refresh(); |
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.
Do we need to refresh? Seems to me that invalidating the queries should be enough to trigger a new fetch?
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.
Can't tell you this at the moment as the API isn't invalidating the data that it should. The refresh was already in the batch claim button and I just made them consistent.
Also, according to the docs invalidating dynamic paths doesn't apply without refetching: https://nextjs.org/docs/14/app/api-reference/functions/revalidatePath
`/profile/${account.address}?tab=hypercerts-claimable`, | ||
`/profile/${account.address}?tab=hypercerts-owned`, | ||
]); | ||
await refreshData(getAddress(account.address!)); |
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.
It seems like we are not invalidating /hypercerts/${selectedHypercert?.hypercert_id}
anymore now.
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.
If I read the correctly, we should precisely specify all API call paths: https://nextjs.org/docs/14/app/api-reference/functions/revalidatePath
correct?
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.
Depends on what you mean by API call path. As I understand it, the paths you specify here are supposed to be frontend paths, and not api paths. The server components that use fetch to get data before being rendered that are in those frontend paths will then have their cache purged, which results in them refetching data when refreshed.
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.
Updated the invalidation methods to match the profile, api and hypcert view pages according to the doc. Again, a bit pending on this one to be completed to validate: hypercerts-org/hypercerts-api#254
Replace useQuery implementation in hypercerts-tab-content and -counts component with hooks for reusability
Removes unused hooks and services from the hypercerts folder
Updates the refreshData methods in the unclaimed hypercerts claim and batch claim button to: - invalidate the user's profile page - invalidate the relevant API paths - all hypercert view pages for the claimed fractions hypercert IDs
- Refactors the refactor into server side data fetching, aiming for as much SSR as possible - Moves actions for hypercerts and allowlist into `actions` folder to make their purpose explicit - Removes the suspense boundary on the hypercerts-tab-content component and puts it on the highest level possible - the UnclaimedFractions table - Adds allowlist action for fetching unclaimed fractions with metadata attached
allowlists/actions/getAllowListRecordsForAddressByClaimedWithMetadata.tsx
Outdated
Show resolved
Hide resolved
components/profile/hypercerts-tab/hypercerts-tab-content-claimable.tsx
Outdated
Show resolved
Hide resolved
|
||
return <UnclaimedHypercertsList unclaimedHypercerts={data} />; | ||
}; | ||
return <UnclaimedHypercertsList unclaimedHypercerts={[...data]} />; |
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.
What's the reason for ...spreading the results here?
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.
The action returns a readonly object and spreading creates a now non-readonly array
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.
This might be too nitpicky but do we know why/where it's marked as readonly? Because this just created an new array with references to the same objects as in the original data
, which end up being mutable (which we shouldn't do anyways).
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.
Because that's what the readFragment
method returns. It ensures typesafety and data consistency imho, we can view our Graph as the SoT and mutating API responses should be explicit. At least, that's how I interpret it.
The cleaner option could be to expect an readonly
array in the component. Which I think is valid but we should apply in more places then
components/profile/hypercerts-tab/hypercerts-tab-content-owned.tsx
Outdated
Show resolved
Hide resolved
- removes the delete action from the metadata fetcher. The requestmap is ephemeral so we let is run with the lifecycle of the method
Refactors the data fetching of the hypercert profile tabs to user server side APIs. Also implements a path invalidation flow that clears cached calls when a fraction claim was succesfully executed