From bc5b173b43a634bab8c234146b1be8ded0bee038 Mon Sep 17 00:00:00 2001 From: Alejandro Celaya Date: Wed, 22 May 2024 16:01:52 +0200 Subject: [PATCH] Allow loading from DataLoader to be cancelled on unmount (#6283) --- .../frontend_apps/components/DataLoader.tsx | 16 ++++++++++++---- .../components/test/DataLoader-test.js | 15 +++++++++++++++ 2 files changed, 27 insertions(+), 4 deletions(-) diff --git a/lms/static/scripts/frontend_apps/components/DataLoader.tsx b/lms/static/scripts/frontend_apps/components/DataLoader.tsx index f8525078fc..1637844e09 100644 --- a/lms/static/scripts/frontend_apps/components/DataLoader.tsx +++ b/lms/static/scripts/frontend_apps/components/DataLoader.tsx @@ -13,8 +13,12 @@ export type DataLoaderProps = { */ children: ComponentChildren; - /** Function that fetches data if {@link loaded} is false. */ - load: () => Promise; + /** + * Function that fetches data if {@link loaded} is false. + * It provides an abort signal that gets aborted when the DataLoader is + * unmounted or the `load` or `onLoad` props change. + */ + load: (signal: AbortSignal) => Promise; /** * Callback to invoke with the results of {@link load}. @@ -46,13 +50,17 @@ export default function DataLoader({ useEffect(() => { if (loaded) { - return; + return () => {}; } - load() + + const controller = new AbortController(); + load(controller.signal) .then(onLoad) .catch(err => { setError(err); }); + + return () => controller.abort(); }, [loaded, load, onLoad]); if (error) { diff --git a/lms/static/scripts/frontend_apps/components/test/DataLoader-test.js b/lms/static/scripts/frontend_apps/components/test/DataLoader-test.js index 5f382d303f..d2fbf6c03f 100644 --- a/lms/static/scripts/frontend_apps/components/test/DataLoader-test.js +++ b/lms/static/scripts/frontend_apps/components/test/DataLoader-test.js @@ -59,4 +59,19 @@ describe('DataLoader', () => { assert.isFalse(wrapper.exists('[data-testid="content"]')); assert.isFalse(wrapper.exists('SpinnerOverlay')); }); + + it('aborts loading when DataLoader is unmounted', () => { + const onAbort = sinon.stub(); + const wrapper = mount( + { + signal.onabort = onAbort; + return 'Hello world'; + }} + />, + ); + + wrapper.unmount(); + assert.called(onAbort); + }); });