Skip to content

Commit

Permalink
fix: Update caching for Identity hooks (#2085)
Browse files Browse the repository at this point in the history
  • Loading branch information
cpcramer authored Mar 6, 2025
1 parent 22601f6 commit 5156cdd
Show file tree
Hide file tree
Showing 10 changed files with 268 additions and 39 deletions.
54 changes: 54 additions & 0 deletions src/identity/hooks/useAvatar.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -134,4 +134,58 @@ describe('useAvatar', () => {
);
});
});

it('respects the enabled option in queryOptions', async () => {
const testEnsName = 'test.ens';
const testEnsAvatar = 'avatarUrl';

mockGetEnsAvatar.mockResolvedValue(testEnsAvatar);

const { result } = renderHook(
() => useAvatar({ ensName: testEnsName }, { enabled: false }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

expect(result.current.isLoading).toBe(false);
expect(result.current.isFetched).toBe(false);
expect(mockGetEnsAvatar).not.toHaveBeenCalled();
});

it('uses the default query options when no queryOptions are provided', async () => {
const testEnsName = 'test.ens';
const testEnsAvatar = 'avatarUrl';

mockGetEnsAvatar.mockResolvedValue(testEnsAvatar);

renderHook(() => useAvatar({ ensName: testEnsName }), {
wrapper: getNewReactQueryTestProvider(),
});

await waitFor(() => {
expect(mockGetEnsAvatar).toHaveBeenCalled();
});
});

it('merges custom queryOptions with default options', async () => {
const testEnsName = 'test.ens';
const testEnsAvatar = 'avatarUrl';
const customStaleTime = 60000;

mockGetEnsAvatar.mockResolvedValue(testEnsAvatar);

const { result } = renderHook(
() => useAvatar({ ensName: testEnsName }, { staleTime: customStaleTime }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

await waitFor(() => {
expect(result.current.data).toBe(testEnsAvatar);
});

expect(mockGetEnsAvatar).toHaveBeenCalled();
});
});
18 changes: 12 additions & 6 deletions src/identity/hooks/useAvatar.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getAvatar } from '@/identity/utils/getAvatar';
import { DEFAULT_QUERY_OPTIONS } from '@/internal/constants';
import { useQuery } from '@tanstack/react-query';
import { mainnet } from 'viem/chains';
import type {
Expand All @@ -14,14 +15,19 @@ export const useAvatar = (
{ ensName, chain = mainnet }: UseAvatarOptions,
queryOptions?: UseQueryOptions,
) => {
const { enabled = true, cacheTime } = queryOptions ?? {};
const { enabled, cacheTime, staleTime, refetchOnWindowFocus } = {
...DEFAULT_QUERY_OPTIONS,
...queryOptions,
};

const queryKey = ['useAvatar', ensName, chain.id];

return useQuery<GetAvatarReturnType>({
queryKey: ['useAvatar', ensName, chain.id],
queryFn: async () => {
return getAvatar({ ensName, chain });
},
queryKey,
queryFn: () => getAvatar({ ensName, chain }),
gcTime: cacheTime,
staleTime,
enabled,
refetchOnWindowFocus: false,
refetchOnWindowFocus,
});
};
69 changes: 51 additions & 18 deletions src/identity/hooks/useName.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,31 +27,24 @@ describe('useName', () => {
it('returns the correct ENS name and loading state', async () => {
const testEnsName = 'test.ens';

// Mock the getEnsName method of the publicClient
mockGetEnsName.mockResolvedValue(testEnsName);

// Use the renderHook function to create a test harness for the useName hook
const { result } = renderHook(() => useName({ address: testAddress }), {
wrapper: getNewReactQueryTestProvider(),
});

// Wait for the hook to finish fetching the ENS name
await waitFor(() => {
// Check that the ENS name and loading state are correct
expect(result.current.data).toBe(testEnsName);
expect(result.current.isLoading).toBe(false);
});
});

it('returns the loading state true while still fetching from ens action', async () => {
// Use the renderHook function to create a test harness for the useName hook
const { result } = renderHook(() => useName({ address: testAddress }), {
wrapper: getNewReactQueryTestProvider(),
});

// Wait for the hook to finish fetching the ENS name
await waitFor(() => {
// Check that the ENS name and loading state are correct
expect(result.current.data).toBe(undefined);
expect(result.current.isLoading).toBe(true);
});
Expand All @@ -60,20 +53,16 @@ describe('useName', () => {
it('returns the correct ENS name and loading state for custom chain ', async () => {
const testEnsName = 'test.customchain.eth';

// Mock the getEnsName method of the publicClient
mockReadContract.mockResolvedValue(testEnsName);

// Use the renderHook function to create a test harness for the useName hook
const { result } = renderHook(
() => useName({ address: testAddress, chain: base }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

// Wait for the hook to finish fetching the ENS name
await waitFor(() => {
// Check that the ENS name and loading state are correct
expect(result.current.data).toBe(testEnsName);
expect(result.current.isLoading).toBe(false);
});
Expand All @@ -83,38 +72,31 @@ describe('useName', () => {
const testCustomChainEnsName = undefined;
const testEnsName = 'ethereum.eth';

// Mock the getEnsName method of the publicClient
mockReadContract.mockResolvedValue(testCustomChainEnsName);
mockGetEnsName.mockResolvedValue(testEnsName);

// Use the renderHook function to create a test harness for the useName hook
const { result } = renderHook(
() => useName({ address: testAddress, chain: base }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

// Wait for the hook to finish fetching the ENS name
await waitFor(() => {
// Check that the ENS name and loading state are correct
expect(result.current.data).toBe(testEnsName);
expect(result.current.isLoading).toBe(false);
});
});

it('returns error for unsupported chain ', async () => {
// Use the renderHook function to create a test harness for the useName hook
const { result } = renderHook(
() => useName({ address: testAddress, chain: optimism }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

// Wait for the hook to finish fetching the ENS name
await waitFor(() => {
// Check that the ENS name and loading state are correct
expect(result.current.data).toBe(undefined);
expect(result.current.isLoading).toBe(false);
expect(result.current.isError).toBe(true);
Expand All @@ -123,4 +105,55 @@ describe('useName', () => {
);
});
});

it('respects the enabled option in queryOptions', async () => {
const testEnsName = 'test.ens';

mockGetEnsName.mockResolvedValue(testEnsName);

const { result } = renderHook(
() => useName({ address: testAddress }, { enabled: false }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

expect(result.current.isLoading).toBe(false);
expect(result.current.isFetched).toBe(false);
expect(mockGetEnsName).not.toHaveBeenCalled();
});

it('uses the default query options when no queryOptions are provided', async () => {
const testEnsName = 'test.ens';

mockGetEnsName.mockResolvedValue(testEnsName);

renderHook(() => useName({ address: testAddress }), {
wrapper: getNewReactQueryTestProvider(),
});

await waitFor(() => {
expect(mockGetEnsName).toHaveBeenCalled();
});
});

it('merges custom queryOptions with default options', async () => {
const testEnsName = 'test.ens';
const customCacheTime = 120000;

mockGetEnsName.mockResolvedValue(testEnsName);

const { result } = renderHook(
() => useName({ address: testAddress }, { cacheTime: customCacheTime }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

await waitFor(() => {
expect(result.current.data).toBe(testEnsName);
});

expect(mockGetEnsName).toHaveBeenCalled();
});
});
18 changes: 12 additions & 6 deletions src/identity/hooks/useName.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getName } from '@/identity/utils/getName';
import { DEFAULT_QUERY_OPTIONS } from '@/internal/constants';
import { useQuery } from '@tanstack/react-query';
import { mainnet } from 'viem/chains';
import type {
Expand All @@ -17,14 +18,19 @@ export const useName = (
{ address, chain = mainnet }: UseNameOptions,
queryOptions?: UseQueryOptions,
) => {
const { enabled = true, cacheTime } = queryOptions ?? {};
const { enabled, cacheTime, staleTime, refetchOnWindowFocus } = {
...DEFAULT_QUERY_OPTIONS,
...queryOptions,
};

const queryKey = ['useName', address, chain.id];

return useQuery<GetNameReturnType>({
queryKey: ['useName', address, chain.id],
queryFn: async () => {
return await getName({ address, chain });
},
queryKey,
queryFn: () => getName({ address, chain }),
gcTime: cacheTime,
staleTime,
enabled,
refetchOnWindowFocus: false,
refetchOnWindowFocus,
});
};
59 changes: 58 additions & 1 deletion src/identity/hooks/useSocials.test.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { getSocials } from '@/identity/utils/getSocials';
import { renderHook } from '@testing-library/react';
import { renderHook, waitFor } from '@testing-library/react';
import { base, mainnet } from 'viem/chains';
import { beforeEach, describe, expect, it, vi } from 'vitest';
import type { Mock } from 'vitest';
Expand Down Expand Up @@ -46,4 +46,61 @@ describe('useSocials', () => {
chain: base,
});
});

it('respects the enabled option in queryOptions', async () => {
const { result } = renderHook(
() => useSocials({ ensName: testEnsName }, { enabled: false }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

expect(result.current.isLoading).toBe(false);
expect(result.current.isFetched).toBe(false);
expect(mockGetSocials).not.toHaveBeenCalled();
});

it('uses the default query options when no queryOptions are provided', async () => {
mockGetSocials.mockResolvedValue({
twitter: 'twitterHandle',
github: 'githubUsername',
farcaster: 'farcasterUsername',
website: 'https://example.com',
});

const { result } = renderHook(() => useSocials({ ensName: testEnsName }), {
wrapper: getNewReactQueryTestProvider(),
});

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(mockGetSocials).toHaveBeenCalled();
});

it('merges custom queryOptions with default options', async () => {
const customStaleTime = 60000;

mockGetSocials.mockResolvedValue({
twitter: 'twitterHandle',
github: 'githubUsername',
farcaster: 'farcasterUsername',
website: 'https://example.com',
});

const { result } = renderHook(
() =>
useSocials({ ensName: testEnsName }, { staleTime: customStaleTime }),
{
wrapper: getNewReactQueryTestProvider(),
},
);

await waitFor(() => {
expect(result.current.isLoading).toBe(false);
});

expect(mockGetSocials).toHaveBeenCalled();
});
});
18 changes: 12 additions & 6 deletions src/identity/hooks/useSocials.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { GetSocialsReturnType, UseQueryOptions } from '@/identity/types';
import { getSocials } from '@/identity/utils/getSocials';
import { DEFAULT_QUERY_OPTIONS } from '@/internal/constants';
import { useQuery } from '@tanstack/react-query';
import type { Chain } from 'viem';
import { mainnet } from 'viem/chains';
Expand All @@ -13,14 +14,19 @@ export const useSocials = (
{ ensName, chain = mainnet }: UseSocialsOptions,
queryOptions?: UseQueryOptions,
) => {
const { enabled = true, cacheTime } = queryOptions ?? {};
const { enabled, cacheTime, staleTime, refetchOnWindowFocus } = {
...DEFAULT_QUERY_OPTIONS,
...queryOptions,
};

const queryKey = ['useSocials', ensName, chain.id];

return useQuery<GetSocialsReturnType>({
queryKey: ['useSocials', ensName, chain.id],
queryFn: async () => {
return getSocials({ ensName, chain });
},
queryKey,
queryFn: () => getSocials({ ensName, chain }),
gcTime: cacheTime,
staleTime,
enabled,
refetchOnWindowFocus: false,
refetchOnWindowFocus,
});
};
2 changes: 2 additions & 0 deletions src/identity/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -280,6 +280,8 @@ export type UseQueryOptions = {
enabled?: boolean;
/** Cache time in milliseconds */
cacheTime?: number;
/** Stale time in milliseconds */
staleTime?: number;
};

/**
Expand Down
Loading

0 comments on commit 5156cdd

Please sign in to comment.