0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Material UI(MUI) + WebComponents でCSSが無効化されるのを回避する方法

Last updated at Posted at 2024-12-06

(2年ほど前のメモが残っていたので記事化…)

前提

  • 普通にReactで書いたコンポーネントを、react-to-webcomponentでウェブコンポーネントに変換した。
  • 親画面とスタイルのスコープを分離したいので、ShadowDOMにする。

環境

  • React 18.1.0
  • TypeScript 4.6.4
  • Material UI(MUI) v5
  • react-to-webcomponent

困ったこと

MUIを使用したウェブコンポーネントを親コンポーネントで表示させると、スタイルが無効化されてしまう(MUI が適用されない)

なぜこのようなことが起きるか

  • 親でウェブコンポーネントを表示させた際、MUIのスタイル定義は親側のstyle要素に挿入される。
  • しかし、ウェブコンポーネントはShadowDOMであるため、親→子にスタイル適用ができずにMUIのスタイル定義が無効化されてしまう。

だめだった実装例

以下のように書くと、Material UIのButtonコンポーネントを使っていても親画面からウェブコンポーネントを表示させた際にスタイルが無効化されてしまい、普通のボタンになってしまう。

const SampleComponent: React.FC<{ data: string }> = ({ data }) => {
    return (
        <div>
            <MuiThemeProvider theme={createTheme()}>
                <Box>
                    // Material UI のButton コンポーネント
					<Button>クリック</Button>
                </Box>
            </MuiThemeProvider>
        </div>
    );
};

SampleComponent.propTypes = {
    data: PropTypes.string,
};
const sampleWebComponent = reactToWebComponent(
    SampleComponent,
    React,
    ReactDOM,
    {
        shadow: 'open',
    }
);
customElements.define('sample-wc', sampleWebComponent);

対応

以下のようなラッパーを定義して、この中で目的のコンポーネントを使う。

Material UI v4での実装

当初はMaterial UI v4 で実装しており、後でアップグレードして後述するv5の実装にしました。

もう需要はあまり無いとは思いますが、v4では以下のような実装になります。

参考 https://stackoverflow.com/questions/54430119/how-to-mount-styles-inside-shadow-root-using-cssinjs-jss

import { CssBaseline, ThemeProvider } from '@material-ui/core';
import { jssPreset, StylesProvider } from '@material-ui/styles';
import { create } from 'jss';
import React, { ReactNode, useState } from 'react';
// theme は MUI のcreateTheme で別途定義したもの
import theme from 'src/theme';

const WebComponentStyle: React.FC<{ children: ReactNode }> = ({
    children,
}) => {
    const [jss, setJss] = useState(null);

    function setRefAndCreateJss(headRef) {
        if (headRef && !jss) {
            const createdJssWithRef = create({
                ...jssPreset(),
                insertionPoint: headRef,
            });
            setJss(createdJssWithRef);
        }
    }

    return (
        <div>
            <style ref={setRefAndCreateJss}></style>
            {jss && (
                <>
                    <CssBaseline />
                    <ThemeProvider theme={theme}>
                        <StylesProvider jss={jss} sheetsManager={new Map()}>
                            {children}
                        </StylesProvider>
                    </ThemeProvider>
                </>
            )}
        </div>
    );
};

MUI v5での実装

上記をv5向けに改修したものが以下になります。

emotionのCacheProviderを使用しています。

import createCache from '@emotion/cache';
import { CacheProvider } from '@emotion/react';
import { CssBaseline, ThemeProvider } from '@mui/material';
import { jssPreset, StylesProvider } from '@mui/styles';
import { create } from 'jss';
import React, { ReactNode, useState } from 'react';
// theme は MUI のcreateTheme で別途定義したもの
import theme from 'src/theme';

const WebComponentStyle: React.FC<{ children: ReactNode }> = ({
    children,
}) => {
    const [jss, setJss] = useState(null);
    const [cache, setCache] = useState(null);

    function setRefAndCreateJss(headRef) {
        if (headRef && !jss) {
            const createdJssWithRef = create({
                ...jssPreset(),
                insertionPoint: headRef,
            });
            setJss(createdJssWithRef);

            const _cache = createCache({
                key: 'css',
                prepend: true,
                container: headRef,
            });
            setCache(_cache);
        }
    }

    return (
        <div>
            <style ref={setRefAndCreateJss}></style>
            {jss && (
                <>
                    <ThemeProvider theme={theme}>
                        <CssBaseline />
                        <StylesProvider jss={jss} sheetsManager={new Map()}>
                            <CacheProvider value={cache}>
                                {children}
                            </CacheProvider>
                        </StylesProvider>
                    </ThemeProvider>
                </>
            )}
        </div>
    );
};

補足

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?