Skip to content

Commit 0f82d1a

Browse files
authored
Add useSuspenseConfigValue hook (#985)
* add useSuspenseConfigValue hook * Fix types for get options
1 parent 7f0a3ef commit 0f82d1a

File tree

6 files changed

+160
-32
lines changed

6 files changed

+160
-32
lines changed
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
import React, { Suspense } from 'react';
2+
3+
import { HttpResponse } from 'msw';
4+
5+
import { act, renderHook, waitFor } from '@/test-utils/rtl';
6+
7+
import useSuspenseConfigValue from '../use-suspense-config-value';
8+
9+
describe(useSuspenseConfigValue.name, () => {
10+
it('should return correct result when data is loaded', async () => {
11+
const { result } = setup({});
12+
13+
await waitFor(() => {
14+
const value = result.current.data;
15+
expect(value).toStrictEqual('mock_config_response');
16+
});
17+
});
18+
19+
it('should throw error if the API route errors out', async () => {
20+
let renderErrorMessage;
21+
try {
22+
await act(async () => {
23+
setup({ error: true });
24+
});
25+
} catch (error) {
26+
if (error instanceof Error) {
27+
renderErrorMessage = error.message;
28+
}
29+
}
30+
31+
expect(renderErrorMessage).toEqual('Failed to fetch config');
32+
});
33+
});
34+
35+
function setup({ error }: { error?: boolean }) {
36+
const { result } = renderHook(
37+
() => {
38+
// @ts-expect-error - using a nonexistent config value
39+
return useSuspenseConfigValue('MOCK_CONFIG_VALUE', { arg: 'value' });
40+
},
41+
{
42+
endpointsMocks: [
43+
{
44+
path: '/api/config',
45+
httpMethod: 'GET',
46+
mockOnce: false,
47+
httpResolver: async () => {
48+
if (error) {
49+
return HttpResponse.json(
50+
{ message: 'Failed to fetch config' },
51+
{ status: 500 }
52+
);
53+
} else {
54+
return HttpResponse.json('mock_config_response');
55+
}
56+
},
57+
},
58+
],
59+
},
60+
{
61+
wrapper: ({ children }) => <Suspense>{children}</Suspense>,
62+
}
63+
);
64+
65+
return { result };
66+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import queryString from 'query-string';
2+
3+
import { type GetConfigKeys } from '@/route-handlers/get-config/get-config.types';
4+
import request from '@/utils/request';
5+
6+
import {
7+
type UseConfigQueryOptions,
8+
type UseConfigValueParams,
9+
} from './use-config-value.types';
10+
11+
export default function getConfigValueQueryOptions<K extends GetConfigKeys>(
12+
params: UseConfigValueParams<K>
13+
): UseConfigQueryOptions<K> {
14+
return {
15+
queryKey: [
16+
'dynamic_config',
17+
{ configKey: params.key, jsonArgs: params.args },
18+
] as const,
19+
queryFn: ({ queryKey: [_, { configKey, jsonArgs }] }) =>
20+
request(
21+
queryString.stringifyUrl({
22+
url: '/api/config',
23+
query: {
24+
configKey,
25+
jsonArgs: JSON.stringify(jsonArgs),
26+
},
27+
}),
28+
{
29+
method: 'GET',
30+
}
31+
).then((res) => res.json()),
32+
};
33+
}
Lines changed: 9 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,30 @@
11
import { useQuery } from '@tanstack/react-query';
2-
import queryString from 'query-string';
32

43
import {
5-
type GetConfigResponse,
64
type GetConfigArgs,
75
type GetConfigKeys,
86
type GetConfigKeysWithArgs,
97
type GetConfigKeysWithoutArgs,
10-
type GetConfigRequestQuery,
118
} from '@/route-handlers/get-config/get-config.types';
12-
import request from '@/utils/request';
13-
import { type RequestError } from '@/utils/request/request-error';
149

10+
import getConfigValueQueryOptions from './get-config-value-query-options';
1511
import { type UseConfigValueResult } from './use-config-value.types';
1612

17-
export default function useConfigValue<K extends GetConfigKeysWithArgs>(
13+
export default function useConfigValue<K extends GetConfigKeysWithoutArgs>(
1814
key: K,
19-
args: GetConfigArgs<K>
15+
args?: GetConfigArgs<K>
2016
): UseConfigValueResult<K>;
2117

22-
export default function useConfigValue<K extends GetConfigKeysWithoutArgs>(
23-
key: K
18+
export default function useConfigValue<K extends GetConfigKeysWithArgs>(
19+
key: K,
20+
args: GetConfigArgs<K>
2421
): UseConfigValueResult<K>;
2522

2623
export default function useConfigValue<K extends GetConfigKeys>(
2724
key: K,
2825
args?: GetConfigArgs<K>
2926
): UseConfigValueResult<K> {
30-
return useQuery<
31-
GetConfigResponse<K>,
32-
RequestError,
33-
GetConfigResponse<K>,
34-
[string, GetConfigRequestQuery<K>]
35-
>({
36-
queryKey: ['dynamic_config', { configKey: key, jsonArgs: args }] as const,
37-
queryFn: ({ queryKey: [_, { configKey, jsonArgs }] }) =>
38-
request(
39-
queryString.stringifyUrl({
40-
url: '/api/config',
41-
query: {
42-
configKey,
43-
jsonArgs: JSON.stringify(jsonArgs),
44-
},
45-
}),
46-
{
47-
method: 'GET',
48-
}
49-
).then((res) => res.json()),
50-
});
27+
return useQuery(
28+
getConfigValueQueryOptions<K>({ key, args: args as GetConfigArgs<K> })
29+
);
5130
}

src/hooks/use-config-value/use-config-value.types.ts

Lines changed: 22 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,12 @@
1-
import { type UseQueryResult } from '@tanstack/react-query';
1+
import {
2+
type UseQueryOptions,
3+
type UseQueryResult,
4+
type UseSuspenseQueryResult,
5+
} from '@tanstack/react-query';
26

37
import {
8+
type GetConfigRequestQuery,
9+
type GetConfigArgs,
410
type GetConfigKeys,
511
type GetConfigResponse,
612
} from '@/route-handlers/get-config/get-config.types';
@@ -10,3 +16,18 @@ export type UseConfigValueResult<K extends GetConfigKeys> = UseQueryResult<
1016
GetConfigResponse<K>,
1117
RequestError
1218
>;
19+
20+
export type UseSuspenseConfigValueResult<K extends GetConfigKeys> =
21+
UseSuspenseQueryResult<GetConfigResponse<K>, RequestError>;
22+
23+
export type UseConfigValueParams<K extends GetConfigKeys> = {
24+
key: K;
25+
args: GetConfigArgs<K>;
26+
};
27+
28+
export type UseConfigQueryOptions<K extends GetConfigKeys> = UseQueryOptions<
29+
GetConfigResponse<K>,
30+
RequestError,
31+
GetConfigResponse<K>,
32+
[string, GetConfigRequestQuery<K>]
33+
>;
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { useSuspenseQuery } from '@tanstack/react-query';
2+
3+
import {
4+
type GetConfigKeys,
5+
type GetConfigArgs,
6+
type GetConfigKeysWithArgs,
7+
type GetConfigKeysWithoutArgs,
8+
} from '@/route-handlers/get-config/get-config.types';
9+
10+
import getConfigValueQueryOptions from './get-config-value-query-options';
11+
import { type UseSuspenseConfigValueResult } from './use-config-value.types';
12+
13+
export default function useSuspenseConfigValue<
14+
K extends GetConfigKeysWithoutArgs,
15+
>(key: K, args?: GetConfigArgs<K>): UseSuspenseConfigValueResult<K>;
16+
17+
export default function useSuspenseConfigValue<K extends GetConfigKeysWithArgs>(
18+
key: K,
19+
args: GetConfigArgs<K>
20+
): UseSuspenseConfigValueResult<K>;
21+
22+
export default function useSuspenseConfigValue<K extends GetConfigKeys>(
23+
key: K,
24+
args?: GetConfigArgs<K>
25+
): UseSuspenseConfigValueResult<K> {
26+
return useSuspenseQuery(
27+
getConfigValueQueryOptions({ key, args: args as GetConfigArgs<K> })
28+
);
29+
}

src/route-handlers/get-config/get-config.types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ export type GetConfigKeys = PublicDynamicConfigKeys;
1010
export type GetConfigKeysWithArgs = PublicConfigKeysWithArgs;
1111
export type GetConfigKeysWithoutArgs = PublicConfigKeysWithoutArgs;
1212

13-
export type GetConfigArgs<K extends PublicDynamicConfigKeys> =
13+
export type GetConfigArgs<K extends GetConfigKeys> =
1414
ArgsOfLoadedConfigResolver<K>;
1515

1616
export type GetConfigRequestQuery<K extends PublicDynamicConfigKeys> = {

0 commit comments

Comments
 (0)