Skip to content

Commit 08e5dca

Browse files
Merge pull request #13 from Optum/useLoadData_error_edge_case
handle `useLoadData` synchronous exception edge case
2 parents 26742ad + c7ff7a5 commit 08e5dca

File tree

2 files changed

+58
-18
lines changed

2 files changed

+58
-18
lines changed

hooks/useLoadData/useLoadData.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -370,4 +370,17 @@ describe('useLoadData', () => {
370370
expect(getFail).toHaveBeenCalledTimes(2);
371371
expect(mockRetry).toHaveBeenCalledTimes(0);
372372
});
373+
374+
it('should set isError to true if the fetch data function throws a non-promise exception', () => {
375+
const {result} = renderHook(() => {
376+
return useLoadData(() => {
377+
// eslint-disable-next-line @typescript-eslint/no-throw-literal
378+
throw 'immediate failure';
379+
});
380+
});
381+
382+
expect(result.current.isInProgress).toBe(false);
383+
expect(result.current.isError).toBe(true);
384+
expect(result.current.error).toBe('immediate failure');
385+
});
373386
});

hooks/useLoadData/useLoadData.ts

Lines changed: 45 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -163,29 +163,56 @@ export function useLoadData<T extends NotUndefined, Deps extends any[]>(
163163
const initialPromise = useMemo(() => {
164164
const correctedArgs = correctOptionalDependencies(fetchDataArgs);
165165
if (!data && counter < 1 && checkArgsAreLoaded(correctedArgs)) {
166-
return fetchData(...((correctedArgs.map(unboxApiResponse) || []) as Parameters<typeof fetchData>));
166+
try {
167+
return {
168+
res: fetchData(...((correctedArgs.map(unboxApiResponse) || []) as Parameters<typeof fetchData>)),
169+
error: undefined
170+
};
171+
} catch (e) {
172+
return {
173+
res: undefined,
174+
error: e
175+
};
176+
}
167177
} else {
168-
return undefined;
178+
return {res: undefined, error: undefined};
169179
}
170180
}, [counter]);
171181

172-
const nonPromiseResult = initialPromise instanceof Promise ? undefined : initialPromise;
182+
const nonPromiseResult = initialPromise.res instanceof Promise ? undefined : initialPromise.res;
173183
const initialData = data || nonPromiseResult;
174184

185+
// Initialize our pending data to one of three possible states:
186+
// 1. If initial data was supplied or if the fetchData function returned a non-Promise value,
187+
// then our initial state will be already "resolved" (not in-progress and not error, we already have the result)
188+
// 2. If initial data was not supplied and fetchData returned a Promise, then our initial state is in-progress
189+
// 3. If initial data was not supplied and fetchData threw a *synchronous* (non-Promise) exception,
190+
// then our initial state is "rejected" (not in-progress and already has an error value)
191+
const initialDataResolved =
192+
initialData &&
193+
({
194+
isInProgress: false,
195+
isError: false,
196+
result: initialData,
197+
error: undefined
198+
} as const);
199+
const initialDataRejected =
200+
initialPromise.error !== undefined &&
201+
({
202+
isInProgress: false,
203+
isError: true,
204+
result: undefined,
205+
error: initialPromise.error
206+
} as const);
207+
const initialDataPending = {
208+
isInProgress: true,
209+
isError: false,
210+
result: undefined,
211+
error: undefined
212+
} as const;
213+
175214
const [pendingData, setPendingData] = useState<ApiResponse<T>>(
176-
initialData
177-
? {
178-
isInProgress: false,
179-
isError: false,
180-
result: initialData,
181-
error: undefined
182-
}
183-
: {
184-
isInProgress: true,
185-
isError: false,
186-
result: undefined,
187-
error: undefined
188-
}
215+
initialDataResolved || initialDataRejected || initialDataPending
189216
);
190217

191218
function retry() {
@@ -224,9 +251,9 @@ export function useLoadData<T extends NotUndefined, Deps extends any[]>(
224251
const unboxedArgs = correctedArgs.map(unboxApiResponse);
225252

226253
const fetchedData =
227-
initialPromise === undefined
254+
initialPromise.res === undefined
228255
? await fetchData(...((unboxedArgs || []) as Parameters<typeof fetchData>))
229-
: await initialPromise;
256+
: await initialPromise.res;
230257

231258
setPendingData({
232259
isInProgress: false,

0 commit comments

Comments
 (0)