Skip to content

Commit 1e1386d

Browse files
committed
Auto retry lazy imports
1 parent a3d5c24 commit 1e1386d

File tree

3 files changed

+50
-6
lines changed

3 files changed

+50
-6
lines changed

services/backend-api/client/src/features/feedConnections/components/DiscordMessageForm/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import {
1717
Text,
1818
} from "@chakra-ui/react";
1919
import { yupResolver } from "@hookform/resolvers/yup";
20-
import { lazy, Suspense, useContext, useState } from "react";
20+
import { Suspense, useContext, useState } from "react";
2121
import { FormProvider, useFieldArray, useForm, useWatch } from "react-hook-form";
2222
import { useTranslation } from "react-i18next";
2323
import { motion } from "framer-motion";
@@ -44,14 +44,15 @@ import {
4444
} from "../../../../contexts/UserFeedConnectionContext";
4545
import getChakraColor from "../../../../utils/getChakraColor";
4646
import { DiscordMessageChannelThreadForm } from "./DiscordMessageChannelThreadForm";
47+
import { lazyWithRetries } from "../../../../utils/lazyImportWithRetry";
4748

48-
const DiscordMessageEmbedForm = lazy(() =>
49+
const DiscordMessageEmbedForm = lazyWithRetries(() =>
4950
import("./DiscordMessageEmbedForm").then(({ DiscordMessageEmbedForm: component }) => ({
5051
default: component,
5152
}))
5253
);
5354

54-
const DiscordChannelConnectionPreview = lazy(() =>
55+
const DiscordChannelConnectionPreview = lazyWithRetries(() =>
5556
import("./DiscordChannelConnectionPreview").then(
5657
({ DiscordChannelConnectionPreview: component }) => ({
5758
default: component,

services/backend-api/client/src/pages/index.tsx

+4-3
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import { Route, Routes, Navigate } from "react-router-dom";
22
import * as Sentry from "@sentry/react";
33
import { Spinner } from "@chakra-ui/react";
4-
import { Suspense, lazy } from "react";
4+
import { Suspense } from "react";
55
import Feed from "./Feed";
66
import FeedFilters from "./FeedFilters";
77
import FeedMessage from "./FeedMessage";
@@ -30,14 +30,15 @@ import { NotFound } from "./NotFound";
3030
import { SuspenseErrorBoundary } from "../components/SuspenseErrorBoundary";
3131
import AddUserFeeds from "./AddUserFeeds";
3232
import { MultiSelectUserFeedProvider } from "../contexts/MultiSelectUserFeedContext";
33+
import { lazyWithRetries } from "../utils/lazyImportWithRetry";
3334

34-
const UserSettings = lazy(() =>
35+
const UserSettings = lazyWithRetries(() =>
3536
import("./UserSettings").then(({ UserSettings: c }) => ({
3637
default: c,
3738
}))
3839
);
3940

40-
const Checkout = lazy(() =>
41+
const Checkout = lazyWithRetries(() =>
4142
import("./Checkout").then(({ Checkout: c }) => ({
4243
default: c,
4344
}))
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
/* eslint-disable no-await-in-loop */
2+
import React from "react";
3+
4+
const RECOGNIZED_ERROR_MESSAGE = "Failed to fetch dynamically imported module: ";
5+
6+
// https://medium.com/@alonmiz1234/retry-dynamic-imports-with-react-lazy-c7755a7d557a
7+
export const lazyWithRetries: typeof React.lazy = (importer) => {
8+
const retryImport = async () => {
9+
try {
10+
return await importer();
11+
} catch (error: any) {
12+
if (!error.message.startsWith(RECOGNIZED_ERROR_MESSAGE)) {
13+
throw error;
14+
}
15+
16+
// retry 5 times with 2 second delay and backoff factor of 2 (2, 4, 8, 16, 32 seconds)
17+
for (let i = 0; i < 5; i += 1) {
18+
await new Promise<void>((resolve) => {
19+
setTimeout(() => {
20+
resolve();
21+
}, 1000 * 2 ** i);
22+
});
23+
// this assumes that the exception will contain this specific text with the url of the module
24+
// if not, the url will not be able to parse and we'll get an error on that
25+
// eg. "Failed to fetch dynamically imported module: https://example.com/assets/Home.tsx"
26+
const url = new URL(error.message.replace(RECOGNIZED_ERROR_MESSAGE, "").trim());
27+
// add a timestamp to the url to force a reload the module (and not use the cached version - cache busting)
28+
url.searchParams.set("t", `${+new Date()}`);
29+
30+
try {
31+
return await import(url.href);
32+
} catch (e) {
33+
console.log("retrying import");
34+
}
35+
}
36+
37+
throw error;
38+
}
39+
};
40+
41+
return React.lazy(retryImport);
42+
};

0 commit comments

Comments
 (0)