はじめに
既存のWebサイトにReactのコンポーネント(react bootstrap)を描画しようとしたところ、bootstrap.cssを読み込む必要があり画面全体のレイアウトが崩れてしまいました
そこで、Reactのコンポーネントを描画するElementのみにbootstrap.cssを適用するため、ShadowDOMを利用してCSSのスコープを独立させてみます
1. createRootを使用してReactのコンポーネントをShadow DOMにレンダリングします
ScopedStyleArea.tsx
import React, { useEffect, useRef } from 'react';
import ReactDOM from 'react-dom/client';
type ScopedStyleAreaProps = {
children: React.ReactNode,
styleSheets?: string[],
}
/**
* スタイルシートを特定の領域のみに反映させ、更にその中にReactのコンポーネントを配置するためのコンポーネント
* ・適用するスタイルシートはpropsで引き渡す
* <ScopedStyleArea styleSheets={['/bootstrap.min.css', '/other.css']}>
* <Button>Button Component</Button>
* </ScopedStyleArea>
*/
const ScopedStyleArea: React.FC<ScopedStyleAreaProps> = ({ children, styleSheets }) => {
const containerRef = useRef<HTMLDivElement | null>(null);
useEffect(() => {
if (containerRef.current) {
// Shadow DOMを作成
const shadowRoot = containerRef.current.shadowRoot || containerRef.current.attachShadow({ mode: 'open' });
// Shadow DOM内にReactコンポーネントをレンダリング
const shadowRootElement = document.createElement('div');
shadowRoot.appendChild(shadowRootElement);
const root = ReactDOM.createRoot(shadowRootElement);
root.render(
<>
{/* CSSの適用 */}
{styleSheets?.map((sheet) => (
<link key={sheet} rel="stylesheet" href={sheet} />
))}
{children}
</>
);
}
}, [children, styleSheets]);
return <div ref={containerRef} />;
};
export default ScopedStyleArea;
2. 利用方法
- ScopedStyleAreaを使ってReactのコンポーネントをShadow DOM内でレンダリングします
- 適用するcssを
styleSheets
に指定します
App.tsx
import {Button} from 'react-bootstrap';
import ScopedStyleArea from './ScopedStyleArea';
function App() {
return (
<ScopedStyleArea styleSheets={['/bootstrap.min.css', '/other.css']}>
<Button>Button Component</Button>
</ScopedStyleArea>
)
}
export default App