Skip to content

feat(ensadmin): introduce useIndexingStatusWithSwr hook#1282

Merged
tk-o merged 9 commits intomainfrom
feat/1200-use-swr-query-hook
Nov 25, 2025
Merged

feat(ensadmin): introduce useIndexingStatusWithSwr hook#1282
tk-o merged 9 commits intomainfrom
feat/1200-use-swr-query-hook

Conversation

@tk-o
Copy link
Copy Markdown
Contributor

@tk-o tk-o commented Nov 19, 2025

Live preview: https://adminensnode-p5chjdg6o-namehash.vercel.app/registrar-actions?connection=https%3A%2F%2Fapi.alpha-sepolia.green.ensnode.io%2F

This PR replaces useIndexingStatus hook in ENSAdmin with a proxy hook called useIndexingStatusWithSwr. The API of both hooks is exactly the same, but behaviour differs.

useIndexingStatus useIndexingStatusWithSwr
Resolves to either IndexingStatusResponseOk or IndexingStatusResponseError. Resolves to IndexingStatusResponseOk.
Resolves to an error when a subsequent data refetch failed Almost always resolves to a success, event if subsequent data refetch failed. The only case when it resolves to an error is when there was no previously successful fetch.
Makes ENSAdmin to present the error. Makes ENSAdmin to present the latest successfully resolved, even when the current refetch failed.

The useIndexingStatusWithSwr hook is based on the new useQuerySwr proxy hook which allows to create a React Query that will always serve previously cached result, and allow refetching the current result in the background.

This hook will suport goals of #1200.

This proxy hook allows to create a React Query which will always serve cached result, and allow refetching the current result in the background.
@tk-o tk-o requested a review from a team as a code owner November 19, 2025 11:15
@changeset-bot
Copy link
Copy Markdown

changeset-bot bot commented Nov 19, 2025

🦋 Changeset detected

Latest commit: b5077fc

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

This PR includes changesets to release 14 packages
Name Type
ensadmin Minor
@ensnode/ensnode-react Minor
ensindexer Minor
ensrainbow Minor
ensapi Minor
@ensnode/datasources Minor
@ensnode/ensrainbow-sdk Minor
@ensnode/ponder-metadata Minor
@ensnode/ensnode-schema Minor
@ensnode/ponder-subgraph Minor
@ensnode/ensnode-sdk Minor
@ensnode/shared-configs Minor
@docs/ensnode Minor
@docs/ensrainbow Minor

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

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Nov 19, 2025

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Preview Comments Updated (UTC)
admin.ensnode.io Ready Ready Preview Comment Nov 25, 2025 6:57pm
ensnode.io Ready Ready Preview Comment Nov 25, 2025 6:57pm
ensrainbow.io Ready Ready Preview Comment Nov 25, 2025 6:57pm

@shrugs
Copy link
Copy Markdown
Collaborator

shrugs commented Nov 19, 2025

did we try something just like

import { keepPreviousData } from '@tanstack/react-query';

const { data, error, isFetching, isError } = useQuery({
    queryKey,
    queryFn,
    staleTime: Infinity,
    gcTime: Infinity,
    placeholderData: keepPreviousData,
  });

// data should always be the last successful (non-throwing) result of queryFn

disclosure, i just looked at the docs and asked claude, so let me know if this was short-sighted and doesn't actually accomplish the goal

@shrugs
Copy link
Copy Markdown
Collaborator

shrugs commented Nov 19, 2025

or perhaps something like this?

const last = useRef<DATA | null>();

const { data, status, ...query } = useQuery({ ... })

if (status === 'success') last.value = data;

return { ...query, status, data: last.value }

@tk-o
Copy link
Copy Markdown
Contributor Author

tk-o commented Nov 20, 2025

@shrugs thanks for offering simpler choices.

keepPreviousData works only during refetching. We also need to cover a case, when a subsequent refetch failed for some reason. In this case, we want the hook to return the cached result from the latest successful response. The query hook needs to mask the fetch error (i.e. isError: false, error: null) and highlight success (isSuccess: true, data: cachedResult).

Also, we can't just set these options:

staleTime: Infinity,
gcTime: Infinity,

Please consider there's no cached result, and the query returns an error. We want this error to be gone (so we need to allow the query to turn stale).

What do you think about those considerations?

@shrugs
Copy link
Copy Markdown
Collaborator

shrugs commented Nov 20, 2025

your assessment is likely correct, you have way more experience with this problem and code than i do, so go with your gut

tk-o added 3 commits November 21, 2025 16:48
Introduces the `useIndexingStatusWithSwr` hook which replaces `useIndexingStatus` hook. The new hook is capable of storing the last OK Response from Indexing Status API and have it served when either the new one cannot be fetched at the moment, or could be be fetched, but its `responseCode` is `error`.
… cached Indexing Status with response code OK.
@tk-o tk-o force-pushed the feat/1200-use-swr-query-hook branch from b81510b to e47b690 Compare November 21, 2025 17:31
@tk-o tk-o changed the title feat(ensnode-react): introduce useSwrQuery hook feat(ensadmin): introduce useIndexingStatusWithSwr hook Nov 23, 2025
Copy link
Copy Markdown
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Hey great to see this enhancement! Shared a few comments with suggestions. Please feel welcome to merge this when ready 👍

IndexingStatusResponseOk,
} from "@ensnode/ensnode-sdk";

const DEFAULT_REFETCH_INTERVAL = 3 * 1000;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Every 3 seconds? Seems quite frequent! Maybe increase to 10 seconds?


/**
* A proxy hook for {@link useIndexingStatus} which applies
* stale-while-revalidate cache for successfully resolved responses.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
* stale-while-revalidate cache for successfully resolved responses.
* stale-while-revalidate cache for successful responses.

);
}

// resolve response to be cached
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// resolve response to be cached
// successful response to be cached

staleTime: cachedResult ? Infinity : undefined,
// cached result can never be removed by garbage collector
gcTime: cachedResult ? Infinity : undefined,
placeholderData: keepPreviousData, // Keep showing previous data during refetch (does not work for errors)
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you please expand upon and clarify in more details what it means when you say: "does not work for errors"?

const memoizedQueryResult = useMemo(() => {
// If the query result is error
// and the cachedResult is available
// override the query result to be success, including cachedResult data
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
// override the query result to be success, including cachedResult data
// override the query result to be success, replacing the unsuccessful result with the most recent successful cachedResult

Is that fair?

Edit: it might also be helpful to rename cachedResult to cachedSuccessfulResult to be more explicit and clear what we're doing here.

tk-o added 2 commits November 25, 2025 16:28
Simplify and document the useSwrQuery hook and its consumers.
Copy link
Copy Markdown
Member

@lightwalker-eth lightwalker-eth left a comment

Choose a reason for hiding this comment

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

@tk-o Great updates. Thank you! Just 2 small questions. Please take the lead to merge when ready 🚀 😄


if (indexingStatusQuery.status === "error") {
if (indexingStatusQuery.isError) {
return <p>Failed to fetch Indexing Status.</p>;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

It seems we should move the the following logic here that this PR is removing a few lines down in this file?

  if (indexingStatus.responseCode === IndexingStatusResponseCodes.Error) {
    return (
      <IndexingStatsShell>
        <IndexingStatsForUnavailableSnapshot />
      </IndexingStatsShell>
    );
  }

Of course, I'm not referring to the logic for IndexingStatusResponseCodes.Error. I'm talking about the HTML that's generated for the case of an error.

case OmnichainIndexingStatusIds.Completed:
return indexingStatusResponseOkOmnichain[selectedVariant];
return indexingStatusResponseOkOmnichain[selectedVariant] as IndexingStatusResponseOk;
case "Response Error":
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

CleanShot 2025-11-25 at 22 42 54

Suggest we clarify what is different about these 2 mock cases.

  • Response Error: Does this case represent receiving a response successfully but it has a non-ok responseCode?
  • Loading Error: Does this case represent having an error receiving a response at all?

Assuming my interpretations above are correct, suggest renaming "Response Error" to "Error ResponseCode"

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Nice suggestion, thank you. It's exactly as you described 👍

update indexing status mocks and the UI used when there is no indexing status available to present
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