-
-
Notifications
You must be signed in to change notification settings - Fork 915
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Shared react #1165
Comments
React is being loaded twice as one of the consumers matches the specific scope while the other one falls back to the global scope. Essentially, your import map is configured as: if request for these modules comes from localhost:3000, use this; otherwise, fall back to global scope.
By default react, and react-dom are externalized in webpack configurations (if created with create-single-spa). They will not be bundled and imports will be left as-is. When you remove them from import maps, browser encounters a line in the code such as "import ... from 'react'", and you get the error as it can not locate "react". |
@MilanKovacic, thank you for your responses! Could you please provide further clarification? I've used a create-react-app and craco(to update webpack) for my single-spa apps. |
This setup could work correctly. In root-config, exclude react, and react-dom from externals so that they are bundled. In microfrontends, set react, and react-dom as externals, and set them in import maps (you do not need to scope them). Best practice is for root-config not to use any framework, and to share dependencies like react accross microfrontends. |
The root app doesn't have any externals, all issues that I described were without externals in the root ap.
We have multiple single-spa apps owned by different teams, and I aimed to use scopes as a means for each team to update their dependencies without affecting other teams' work.
This is our goal. Root app is a monolith legacy, we're gradually working on separating it step by step. However, this process will take some time. |
I am not sure what the issue is. This is an expected behavior as you have one react in global scope of the import one, and one scoped react. All requests originating from https://localhost:3000/ will utilize the scoped one, while all other consumers will utilize the global one, which could lead to multiple versions/instances being loaded.
This is happening because you have react/react-dom externalized in one of the applications.
You can use scopes for this. This is an organizational decision — be careful of microfrontend anarchy. |
But I have only one consumer. In my example, there is one single-spa app. The root app uses react from the
But I have scoped react.
With this import-map I'm getting the error |
Please post full root-config index file, and webpack/craco configurations. |
Webpack config for single-spa app(i've removed jest configuration) const singleSpaApplicationPlugin = require('craco-plugin-single-spa-application');
const orgName = 'org';
const projectName = 'singleSpa';
module.exports = {
webpack: {
plugins: {
remove: ['ManifestPlugin'],
},
configure: {
externals: ['react', 'react-dom']
}
},
plugins: [
{
plugin: {
...singleSpaApplicationPlugin,
overrideCracoConfig: ({ cracoConfig, pluginOptions }) => {
const cracoConfigOverride =
singleSpaApplicationPlugin.overrideCracoConfig({
cracoConfig,
pluginOptions,
});
// Don't remove HtmlWebpackPlugin for ephemerals to work
cracoConfigOverride.webpack.plugins.remove =
cracoConfigOverride.webpack.plugins.remove.filter(
(el) => el !== 'HtmlWebpackPlugin',
);
return cracoConfigOverride;
},
},
options: {
orgName,
projectName,
entry: `src/${orgName}-${projectName}`,
orgPackagesAsExternal: false,
reactPackagesAsExternal: false,
minimize: process.env.NODE_ENV === 'production',
rootDirectoryLevel: 1,
},
},
],
}; Root config file. We use it as a separate entry in the webpack entry: {
reactapp: [path.join(__dirname, 'index.jsx')],
mfe: [path.join(__dirname, 'root-config.js')],
}, There is more import { registerApplication, start, addErrorHandler } from 'single-spa';
window.addEventListener('single-spa:app-change', event => {
const { appsByNewStatus } = event.detail;
// As core styles were overwriting our styles, this is the solution we came up with:
// Looking for core styles index
const coreStylesIndex = [...document.styleSheets].findIndex(
styleSheet =>
styleSheet.href && styleSheet.href.includes('/css/app.css'),
);
// If it is micro FE that does not need core styles, then set it to true
if (coreStylesIndex >= 0) {
const isDisabled = appsByNewStatus.MOUNTED.some(mountedApp =>
DISABLED_CORE_STYLES_APPS.includes(mountedApp),
);
document.styleSheets[coreStylesIndex].disabled = isDisabled;
}
});
window.addEventListener('single-spa:app-change', event => {
const { newAppStatuses } = event.detail;
console.log('Some applications were mounted/unmounted: ');
console.log(newAppStatuses);
});
window.addEventListener('single-spa:routing-event', event => {
const { newAppStatuses } = event.detail;
console.log(newAppStatuses); // { app1: MOUNTED, app2: NOT_MOUNTED }
});
registerApplication({
name: WEB_APP_NAME,
app: async () => {
if (process.env.NODE_ENV === 'production') {
await System.import(`${WEB_APP_NAME}/config`);
}
return System.import(WEB_APP_NAME);
},
activeWhen: location => {
const { pathname } = location;
return pathname.startsWith(`${prefix}/singleSpa`);
},
customProps: () => {
const context = document.getElementById(
`single-spa-application:${WEB_APP_NAME}`,
)?.dataset;
return {
isEphemeralEnv: formatBool(context?.isEphemeralEnv),
allFeatureFlags: { ...allFeatureFlags },
};
},
});
localStorage.setItem('devtools', 'false');
addErrorHandler(err => {
System.delete(System.resolve(err.appOrParcelName));
});
start(); |
Please add the index.html file. |
I've removed some sensitive data {% load i18n %}
<!doctype html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1" />
<script src="//ajax.googleapis.com/ajax/libs/webfont/1.5.6/webfont.js"></script>
<script src={{JARIS_CDN_URL}}></script>
<script>
WebFont.load({
google: {
families: ['Montserrat']
},
typekit: {
id: 'pmn7pbb'
}
});
</script>
{% block css %}
<link rel="preconnect" href="https://fonts.gstatic.com">
<link href="https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap" rel="stylesheet">
{% endblock %}
<meta http-equiv="X-UA-Compatible" content="IE=edge,chrome=1" />
<meta name="application-name" content="HotSpot" />
<meta name="apple-mobile-web-app-capable" content="yes" />
<meta name="apple-mobile-web-app-status-bar-style" content="black" />
<meta name="format-detection" content="telephone=no" />
<meta name="format-detection" content="email=no" />
<meta name="msapplication-config" content="images/favicons/browserconfig.xml">
<meta name="theme-color" content="#f9f9f9">
<meta id="show-tour-intercom" data-value="{{ SHOW_TOUR }}">
<meta id="base-url-intercom" data-value="{{ BASE_URL}}">
<meta id="intercom-tour-id" data-value="{{ INTERCOM_TOUR_ID }}">
{% comment %}
***************************************************
Setup our MicroFE's!!!
***************************************************
{% endcomment %}
<script src="https://cdn.jsdelivr.net/npm/regenerator-runtime@0.13.7/runtime.min.js"></script>
<meta name="importmap-type" content="systemjs-importmap" />
<script type="systemjs-importmap">
{
"imports": {
"single-spa": "https://cdn.jsdelivr.net/npm/single-spa@5.9.3/lib/system/single-spa.min.js",
"react-router-dom": "https://cdn.jsdelivr.net/npm/react-router-dom@5.2.0/umd/react-router-dom.min.js",
"@root-config": "{{MFE_JS_URL}}"
},
"scopes": {
"https://localhost:3000/": {
"react": "https://cdn.jsdelivr.net/npm/@esm-bundle/react@17.0.2/system/react.production.min.js",
"react-dom": "https://cdn.jsdelivr.net/npm/@esm-bundle/react-dom@17.0.2/system/react-dom.production.min.js"
}
}
}
</script>
<script type="systemjs-importmap" src="{{IMPORT_MAP_URL}}"></script>
<link rel="preload" href="https://cdn.jsdelivr.net/npm/single-spa@5.9.3/lib/system/single-spa.min.js" as="script" crossOrigin="anonymous">
<script src="https://cdn.jsdelivr.net/npm/import-map-overrides@2.2.0/dist/import-map-overrides.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/system.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/systemjs@6.8.3/dist/extras/amd.min.js"></script>
<script type="application/javascript">
window.apiUrl = '{{ API_URL }}';
</script>
{% if should_mfe_wait_for_dom %}
<script>
window.addEventListener('DOMContentLoaded', function(){
System.import('@root-config');
});
</script>
{% else %}
<script>
System.import('@root-config');
</script>
{% endif %}
</head>
<body>
<import-map-overrides-full show-when-local-storage="devtools" dev-libs></import-map-overrides-full>
<script async src="//www.google-analytics.com/analytics.js"></script>
<script src="{{ APP_JS_URL }}"></script>
<script src="{{ REACTAPP_JS_URL }}" data-name="react-app" type="application/javascript"></script>
</body>
</html> |
@MilanKovacic Hi! I managed to reproduce it with the default |
Hi, great. I was busy, but now it should be easier to locate the issue. |
Hi, did you manage to solve the issue? |
Describe the bug or question
I have two issues with sharing react, they might be related, not sure.
After I open the page with
![image](https://private-user-images.githubusercontent.com/91183084/283494613-07e63280-dd1c-4d86-a967-2d7eb6073b10.png?jwt=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJnaXRodWIuY29tIiwiYXVkIjoicmF3LmdpdGh1YnVzZXJjb250ZW50LmNvbSIsImtleSI6ImtleTUiLCJleHAiOjE3MjA1MjU4MDcsIm5iZiI6MTcyMDUyNTUwNywicGF0aCI6Ii85MTE4MzA4NC8yODM0OTQ2MTMtMDdlNjMyODAtZGQxYy00ZDg2LWE5NjctMmQ3ZWI2MDczYjEwLnBuZz9YLUFtei1BbGdvcml0aG09QVdTNC1ITUFDLVNIQTI1NiZYLUFtei1DcmVkZW50aWFsPUFLSUFWQ09EWUxTQTUzUFFLNFpBJTJGMjAyNDA3MDklMkZ1cy1lYXN0LTElMkZzMyUyRmF3czRfcmVxdWVzdCZYLUFtei1EYXRlPTIwMjQwNzA5VDExNDUwN1omWC1BbXotRXhwaXJlcz0zMDAmWC1BbXotU2lnbmF0dXJlPWY5Yzg1MTIyZGVhN2M1YWRiZDM4Yjg0YmIwNzdlMjM3MzkyOTMyMTA3YTA0OGYwZjIzYzM5ZmFmNzA1ZTE4NGYmWC1BbXotU2lnbmVkSGVhZGVycz1ob3N0JmFjdG9yX2lkPTAma2V5X2lkPTAmcmVwb19pZD0wIn0.WoTHyGqIwLuySWNgF3rB9eZzzd7p5KBfXZagNQsh-hI)
single-spa-react
app it loads react twicereact
andreact-dom
fromimports
, I'm getting an errorUnable to resolve bare specifier 'react' from https://cdn.jsdelivr.net/npm/@esm-bundle/react-dom@17.0.2/system/react-dom.production.min.js
With the import-map without scopes all works perfectly
What can be the problem?
The text was updated successfully, but these errors were encountered: