diff --git a/packages/core/admin/admin/src/StrapiApp.js b/packages/core/admin/admin/src/StrapiApp.js
index 36867db510..117accf157 100644
--- a/packages/core/admin/admin/src/StrapiApp.js
+++ b/packages/core/admin/admin/src/StrapiApp.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import * as React from 'react';
import { darkTheme, lightTheme } from '@strapi/design-system';
import invariant from 'invariant';
@@ -114,14 +114,30 @@ class StrapiApp {
);
invariant(
link.Component && typeof link.Component === 'function',
- `link.Component should be a valid React Component`
+ `link.Component must be a function returning a Promise. Please use: \`Component: () => import(path)\` instead.`
);
invariant(
link.icon && typeof link.icon === 'function',
`link.Icon should be a valid React Component`
);
- this.menu.push(link);
+ if (
+ link.Component &&
+ typeof link.Component === 'function' &&
+ link.Component[Symbol.toStringTag] === 'AsyncFunction'
+ ) {
+ console.warn(`
+ [deprecated] addMenuLink() was called with an async Component from the plugin "${link.intlLabel.Internationalization}". This will be removed
+ in the future. Please use: \`Component: () => import(path)\` instead.
+ `);
+ }
+
+ this.menu.push({
+ ...link,
+
+ // React.lazy can be removed once we migrate to react-router@6, because the component can handle it natively
+ Component: React.lazy(link.Component),
+ });
};
addMiddlewares = (middlewares) => {
@@ -149,10 +165,26 @@ class StrapiApp {
invariant(link.to, `link.to should be defined for ${stringifiedLink}`);
invariant(
link.Component && typeof link.Component === 'function',
- `link.Component should be a valid React Component`
+ `link.Component must be a function returning a Promise. Please use: \`Component: () => import(path)\` instead.`
);
- this.settings[sectionId].links.push(link);
+ if (
+ link.Component &&
+ typeof link.Component === 'function' &&
+ link.Component[Symbol.toStringTag] === 'AsyncFunction'
+ ) {
+ console.warn(`
+ [deprecated] addSettingsLink() was called with an async Component from the plugin: "${link.intlLabel.Internationalization}". This will be removed
+ in the future. Please use: \`Component: () => import(path)\` instead.
+ `);
+ }
+
+ this.settings[sectionId].links.push({
+ ...link,
+
+ // React.lazy can be removed once we migrate to react-router@6, because the component can handle it natively
+ Component: React.lazy(link.Component),
+ });
};
addSettingsLinks = (sectionId, links) => {
diff --git a/packages/core/admin/admin/src/components/Providers/index.js b/packages/core/admin/admin/src/components/Providers/index.js
index 412891cbc7..e7c10d5738 100644
--- a/packages/core/admin/admin/src/components/Providers/index.js
+++ b/packages/core/admin/admin/src/components/Providers/index.js
@@ -111,7 +111,8 @@ Providers.propTypes = {
defaultMessage: PropTypes.string.isRequired,
}).isRequired,
permissions: PropTypes.array,
- Component: PropTypes.func,
+ // React.lazy loadable
+ Component: PropTypes.object,
})
).isRequired,
menuLogo: PropTypes.oneOfType([PropTypes.string, PropTypes.any]).isRequired,
diff --git a/packages/core/admin/admin/src/pages/Admin/index.js b/packages/core/admin/admin/src/pages/Admin/index.js
index dd0129a3d1..2463a7f54d 100644
--- a/packages/core/admin/admin/src/pages/Admin/index.js
+++ b/packages/core/admin/admin/src/pages/Admin/index.js
@@ -16,7 +16,6 @@ import { Route, Switch } from 'react-router-dom';
import LeftMenu from '../../components/LeftMenu';
import useConfigurations from '../../hooks/useConfigurations';
import useMenu from '../../hooks/useMenu';
-import { createRoute } from '../../utils/createRoute';
import { SET_APP_RUNTIME_STATUS } from '../App/constants';
const CM = React.lazy(() =>
@@ -76,25 +75,6 @@ export const Admin = () => {
}
}, [appStatus, dispatch, trackUsage]);
- const routes = menu
- .filter((link) => link.Component)
-
- /**
- * `Component` is an async function, which is passed as property of the
- * addMenuLink() API during the plugin registration step.
- *
- * Because of that we can't just render ,
- * but have to await the function.
- *
- * This isn't a good React pattern and should be reconsidered.
- */
-
- .map(({ to, Component, exact }) => createRoute(Component, to, exact));
-
- if (isLoading) {
- return ;
- }
-
return (
@@ -104,18 +84,31 @@ export const Admin = () => {
/>
- }>
+ {isLoading ? (
+
+ ) : (
- {routes}
+ {menu.map(({ to, Component, exact }) => (
+ (
+ }>
+
+
+ )}
+ key={to}
+ path={to}
+ exact={exact || false}
+ />
+ ))}
-
+ )}
{/* TODO: we should move the logic to determine whether the guided tour is displayed
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/constants.js b/packages/core/admin/admin/src/pages/SettingsPage/constants.js
index 2ade0f5bbf..3aaf3315c8 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/constants.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/constants.js
@@ -2,67 +2,67 @@ import * as React from 'react';
export const SETTINGS_ROUTES_CE = [
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-roles-list" */ './pages/Roles/ProtectedListPage')
),
path: '/settings/roles',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/CreatePage')
),
path: '/settings/roles/duplicate/:id',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/CreatePage')
),
path: '/settings/roles/new',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-edit-roles-page" */ './pages/Roles/ProtectedEditPage')
),
path: '/settings/roles/:id',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-users" */ './pages/Users/ProtectedListPage')
),
path: '/settings/users',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "admin-edit-users" */ './pages/Users/ProtectedEditPage')
),
path: '/settings/users/:id',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "webhook-edit-page" */ './pages/Webhooks/ProtectedCreateView')
),
path: '/settings/webhooks/create',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "webhook-edit-page" */ './pages/Webhooks/ProtectedEditView')
),
path: '/settings/webhooks/:id',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "webhook-list-page" */ './pages/Webhooks/ProtectedListView')
),
path: '/settings/webhooks',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "api-tokens-list-page" */ './pages/ApiTokens/ProtectedListView')
),
path: '/settings/api-tokens',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(
/* webpackChunkName: "api-tokens-create-page" */ './pages/ApiTokens/ProtectedCreateView'
)
@@ -70,13 +70,13 @@ export const SETTINGS_ROUTES_CE = [
path: '/settings/api-tokens/create',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(/* webpackChunkName: "api-tokens-edit-page" */ './pages/ApiTokens/ProtectedEditView')
),
path: '/settings/api-tokens/:id',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(
/* webpackChunkName: "transfer-tokens-create-page" */ './pages/TransferTokens/ProtectedCreateView'
)
@@ -84,7 +84,7 @@ export const SETTINGS_ROUTES_CE = [
path: '/settings/transfer-tokens/create',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(
/* webpackChunkName: "transfer-tokens-list-page" */ './pages/TransferTokens/ProtectedListView'
)
@@ -92,7 +92,7 @@ export const SETTINGS_ROUTES_CE = [
path: '/settings/transfer-tokens',
},
{
- component: React.lazy(() =>
+ Component: React.lazy(() =>
import(
/* webpackChunkName: "transfer-tokens-edit-page" */ './pages/TransferTokens/ProtectedEditView'
)
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/index.js
index d502b7152e..428260f2b2 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/index.js
@@ -8,7 +8,6 @@ import { Redirect, Route, Switch, useParams } from 'react-router-dom';
import { useSettingsMenu } from '../../hooks';
import { useEnterprise } from '../../hooks/useEnterprise';
-import { createRoute } from '../../utils/createRoute';
import SettingsNav from './components/SettingsNav';
import { SETTINGS_ROUTES_CE } from './constants';
@@ -31,26 +30,6 @@ export function SettingsPage() {
}
);
- /**
- * `Component` is an async function, which is passed as property of the
- * addSettingsLink() API during the plugin bootstrap step.
- *
- * Because of that we can't just render ,
- * but have to await the function.
- *
- * This isn't a good React pattern and should be reconsidered.
- */
-
- const pluginSettingsRoutes = Object.values(settings).flatMap((section) =>
- section.links.map((link) => createRoute(link.Component, link.to, link.exact || false))
- );
-
- // Since the useSettingsMenu hook can make API calls in order to check the links permissions
- // We need to add a loading state to prevent redirecting the user while permissions are being checked
- if (isLoading) {
- return ;
- }
-
if (!settingId) {
return ;
}
@@ -64,15 +43,49 @@ export function SettingsPage() {
})}
/>
-
-
+ {isLoading ? (
+
+ ) : (
+
+ (
+ }>
+
+
+ )}
+ exact
+ />
- {routes.map(({ path, component }) => (
-
- ))}
+ {routes.map(({ path, Component }) => (
+ (
+ }>
+
+
+ )}
+ exact
+ />
+ ))}
- {pluginSettingsRoutes}
-
+ {Object.values(settings).flatMap((section) =>
+ section.links.map(({ Component, to, exact }) => (
+ (
+ }>
+
+
+ )}
+ key={to}
+ path={to}
+ exact={exact || false}
+ />
+ ))
+ )}
+
+ )}
);
}
diff --git a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js
index d863c007df..b8ac35a952 100644
--- a/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js
+++ b/packages/core/admin/admin/src/pages/SettingsPage/pages/ApplicationInfosPage/index.js
@@ -9,11 +9,11 @@ import {
HeaderLayout,
Layout,
Link,
- Loader,
Main,
Typography,
} from '@strapi/design-system';
import {
+ LoadingIndicatorPage,
prefixFileUrlWithBackendUrl,
SettingsPageTitle,
useAPIErrorHandler,
@@ -161,12 +161,7 @@ const ApplicationInfosPage = () => {
{isLoading ? (
-
- {formatMessage({
- id: 'Settings.application.isLoading',
- defaultMessage: 'Loading',
- })}
-
+
) : (