Skip to content
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

Can this be used in a single spa application? #69

Closed
kennyist opened this issue Mar 15, 2022 · 13 comments
Closed

Can this be used in a single spa application? #69

kennyist opened this issue Mar 15, 2022 · 13 comments

Comments

@kennyist
Copy link

kennyist commented Mar 15, 2022

I'm having issues using tss-react in a single spa environment. In my root app, I am able to get the UMD version of MUI and emotion (not been able to find an umd/systemjs version of this package or create one successfully):

        "@mui/material": "https://unpkg.com/@mui/material@5.4.3/umd/material-ui.production.min.js",
        "@emotion/react": "https://cdn.jsdelivr.net/npm/@emotion/react@11.8.2/dist/emotion-react.umd.min.js",
        "@emotion/styled": "https://cdn.jsdelivr.net/npm/@emotion/styled@11.8.1/dist/emotion-styled.umd.min.js",

And they are being externalised by webpack in the sub app I am trying to use tss-react, In this app I have created a cache as the docs showed:

export const muiCache = createCache({
    'key': 'mui',
    'prepend': true,
  });

// region Component

const Theme: FunctionComponent<IThemeProps> = ({
  children,

  style
}) => {
  // handlers

  const theme = React.useMemo(() => {
    switch (style) {
      case Themes.Default:
      default:
        return DefaultTheme;
    }
  }, [style]);

  // render

  return (
    <CacheProvider value={muiCache}>
      <MuiThemeProvider theme={theme}>
        {children}
      </MuiThemeProvider>
    </CacheProvider>
  );
};

But MUI css always takes precedence over tss-react created styles and cannot seem to get it to work. Is there a way to get this to work?

@kennyist kennyist changed the title Can this be used in a single spa context? Can this be used in a single spa application? Mar 15, 2022
@garronej
Copy link
Owner

garronej commented Mar 15, 2022

Hello @kennyist,
Is the code of your your repo open source?

@garronej
Copy link
Owner

There is this example of tss-react setup on a SPA: https://github.com/garronej/tss-react/tree/main/src/test/apps/spa

you can run it with:

git clone https://github.com/garronej/tss-react
cd tss-react
yarn
yarn build
yarn start_spa

Regarding why it's not working in your case, I can't know with the fiew informations you provided me.
I think you have multiple copy of @emotion/react in your dependency and MUI is not picking up your cache. You chan change the key by something random and go see in the debug devloper console if you see it appear.
I will will gladly debug your depo if you provide me the code or a sandbox.

@kennyist
Copy link
Author

Hi @garronej, thanks for the reply. Unfortunately cannot give access to the main repo, But I have made a quick setup in the same style being used at the moment, with it also not working there. This is using single spa for microservice setup.

@garronej
Copy link
Owner

Thank you for setting up this sample repo.
Unfortunately I can't get it to run.
I did:

git clone https://github.com/kennyist/tss-react-test
cd tss-react-rest
cd ui
npm install
npm run npm run start:standalone

I am getting this:

image

Please provide additional informations to help me reproduce the specifical issue you are facing related to tss-react

@kennyist
Copy link
Author

kennyist commented Mar 15, 2022

Ah sorry, You need to clone the repo and then do this for each sub folder (ui, root, page)

cd tss-react-test
cd <SUB FOLDER>
npm install
npm start

Once all are running at the same time, go to http://localhost:9000. You should then get a MUI button, This has a className style to change the background to red that 'MUI' overrides.

@garronej
Copy link
Owner

garronej commented Mar 15, 2022

Hi @kennyist,
So, I spend an hour trying to sort this out I understand what the problem is but unfortunately I am not familiar enough with single-spa to provide a working setup.
The problem is not related to TSS react, @emotion/react is supposed to be a dependency shared by @mui/material and tss-react. Like react or react-dom are shared across the App.
In your setup @mui/material internally uses an other instance of @emotion/react that you are when you import { CacheProvider } from "@emotion/react".
As a result, when you provide you cache with<CacheProvider> it is never picked up by @mui/material.

This is a problem not only for TSS but also potentially for other libraries you might be using.
I'd suggest submitting an issue to single-spa and/or emotion.

Please let me know it you manage to sort it out.
Best

@garronej
Copy link
Owner

garronej commented Mar 20, 2022

Hi @kennyist. Have you find a solution to this?
When this is is done I think the problem will solve itself but still, I would like to figure out why @emotion/react can't be shared even tho you seems to have setup single-spa well...

gitbook-com bot pushed a commit that referenced this issue Mar 20, 2022
@liztownd
Copy link

Hi @kennyist - just wanted to check to see if you've had any success with this issue? I'm running into the same thing with my single-spa app (set up very similarly to your test repo you linked above) and cannot make the tss-styles override the MUI generated css-styles (have tried <StyledEngineProvider injectFirst> as well as the recommended <CacheProvider> set up). Have you had any luck figuring out a workaround? Thanks!

@garronej
Copy link
Owner

Hi @liztownd,
I am sorry I didn't get the chance to try again.
There is still this MUI PR going on that should fix this issue.
For now all I can tell is that this is the root cause:

The problem is not related to TSS react, @emotion/react is supposed to be a dependency shared by @mui/material and > tss-react. Like react or react-dom are shared across the App.
In your setup @mui/material internally uses an other instance of @emotion/react that you are when you
import { CacheProvider } from "@emotion/react".
As a result, when you provide you cache with it is never picked up by @mui/material.

If you manage to setup single-spa in such a way that @emotion/react is not duplicated in your code, you win.

@liztownd
Copy link

liztownd commented Apr 28, 2022

@garronej & @kennyist -- I WIN! Lol.

In the root-config file when registering the sub applications I passed an instance of the createCache & CacheProvider from @emotion, then picked them up from the props rather than importing them in each sub-app (I had several apps all importing their own theme object and assigning classNames). Voila, I now have one muiCache, and the styles are appearing in order, pre-prending the tss styles, each with their own prefix depending on the sub-app they originated from.

in root-config:

import { registerApplication, start } from "single-spa";
import {
  constructApplications,
  constructRoutes,
  constructLayoutEngine,
} from "single-spa-layout";
import microfrontendLayout from "./microfrontend-layout.html";
import { CacheProvider } from "@emotion/react";
import createCache from "@emotion/cache";

const routes = constructRoutes(microfrontendLayout);
const applications = constructApplications({
  routes,
  loadApp({ name }) {
    return System.import(name);
  },
});
const layoutEngine = constructLayoutEngine({ routes, applications });

const applicationsWithProps = applications.map((application) => {
  return {
    ...application,
    customProps: { createCache: createCache, CacheProvider: CacheProvider },
  };
});

applicationsWithProps.forEach(registerApplication);

layoutEngine.activate();
start();

inside cache provider in every sub app:

import { ReactChild } from "react";
import type { CacheProvider } from "@emotion/react";
import type createCache from "@emotion/cache";

export type Props = {
  CacheProvider: typeof CacheProvider;
  createCache: typeof createCache;
  children?: ReactChild;
};

export function Theme(props: Props) {

  const { CacheProvider, createCache, children } = props;

  const muiCache = createCache({
    "key": "mui", // to test in each sub app I assigned a different "key", they should all show up in one cache
    "prepend": false
  });

  return <CacheProvider value={muiCache}>{children}</CacheProvider>;

};

@garronej
Copy link
Owner

@liztownd awesome, nicely done!
Thank you very much for your contribution.
I will add a section in the documentation website.

@tristan-cunningham
Copy link

tristan-cunningham commented Apr 29, 2022

@liztownd
@garronej Documentation may need to change if you have made it

Your solution causes single spa inspector dev tool to not work, infinite loading. This seems like it will happen if you use single spa layout engine.

To fix this, don't create a new array with custom props added (applicationsWithProps in your example). Add the props to the route constructor like so:

const routes = constructRoutes(microfrontendLayout, {
  props: {
    createCache: createCache,
    CacheProvider: CacheProvider
  },
  loaders: {
  }
});

And then in your layout html file add the props to the application like this:

<application name="@your-app" props="createCache,CacheProvider"></application>

@liztownd
Copy link

@kennyist nice catch! Thank you! I'm new to single-spa (haven't even installed the single spa inspector dev tools yet) so this is super helpful!

gitbook-com bot pushed a commit that referenced this issue Nov 15, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants