Skip to content

Commit 010654f

Browse files
fix(core): correct useCustom return type during loading
- Changed result.data to return undefined instead of empty object while loading - Updated type to CustomResponse<TData>[data] | undefined - Added test to verify undefined data during loading state Resolves #7088
1 parent 3edacb0 commit 010654f

3 files changed

Lines changed: 49 additions & 4 deletions

File tree

.changeset/four-ads-sin.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
---
2+
"@refinedev/core": patch
3+
---
4+
5+
fix(core): correct `useCustom` return type to reflect undefined data during loading #7088
6+
7+
Fixed a type safety issue where `useCustom` returned an empty object (`{}`) while loading but TypeScript type was `CustomResponse<TData>["data"]`. This caused runtime errors like `result.data.map is not a function` when accessing data without proper checks. Now `result.data` returns `undefined` while loading with type `CustomResponse<TData>["data"] | undefined`, requiring proper null checks like `result.data?.map()`.
8+
9+
Resolves #7088

packages/core/src/hooks/data/useCustom.spec.tsx

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,9 +27,47 @@ describe("useCustom Hook", () => {
2727

2828
const { data } = result.current.result;
2929

30+
expect(data).toBeDefined();
3031
expect(data).toHaveLength(2);
3132
});
3233

34+
it("should return undefined data while loading", async () => {
35+
const { result } = renderHook(
36+
() =>
37+
useCustom({
38+
url: "remoteUrl",
39+
method: "get",
40+
}),
41+
{
42+
wrapper: TestWrapper({
43+
dataProvider: {
44+
default: {
45+
...MockJSONServer.default,
46+
custom: () => {
47+
return new Promise((res) => {
48+
setTimeout(() => res({ data: [1, 2, 3] } as any), 100);
49+
});
50+
},
51+
},
52+
},
53+
resources: [{ name: "posts" }],
54+
}),
55+
},
56+
);
57+
58+
// While loading, data should be undefined
59+
expect(result.current.query.isPending).toBeTruthy();
60+
expect(result.current.result.data).toBeUndefined();
61+
62+
await waitFor(() => {
63+
expect(result.current.query.isSuccess).toBeTruthy();
64+
});
65+
66+
// After loading, data should be defined
67+
expect(result.current.result.data).toBeDefined();
68+
expect(result.current.result.data).toEqual([1, 2, 3]);
69+
});
70+
3371
describe("without custom query key", () => {
3472
const config = { sorters: [{ field: "id", order: "desc" }] } as any;
3573
const meta = { meta: "meta" };

packages/core/src/hooks/data/useCustom.ts

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -110,12 +110,10 @@ export type UseCustomProps<TQueryFnData, TError, TQuery, TPayload, TData> = {
110110
export type UseCustomReturnType<TData, TError> = {
111111
query: QueryObserverResult<CustomResponse<TData>, TError>;
112112
result: {
113-
data: CustomResponse<TData>["data"];
113+
data: CustomResponse<TData>["data"] | undefined;
114114
};
115115
} & UseLoadingOvertimeReturnType;
116116

117-
const EMPTY_OBJECT = Object.freeze({}) as any;
118-
119117
export const useCustom = <
120118
TQueryFnData extends BaseRecord = BaseRecord,
121119
TError extends HttpError = HttpError,
@@ -233,7 +231,7 @@ export const useCustom = <
233231
return {
234232
query: queryResponse,
235233
result: {
236-
data: queryResponse.data?.data || EMPTY_OBJECT,
234+
data: queryResponse.data?.data,
237235
},
238236
overtime: { elapsedTime },
239237
};

0 commit comments

Comments
 (0)