我认为这里的问题是将emotion 生成的样式应用于放置在 iframe 内的按钮。
我发现 Mitchell(情感核心团队)的这个出色的例子完全符合您的需要:github
这里是您的代码箱的一个分支,其中包含复制的代码,以及一个基本的自制 <Iframe> 元素:codesandbox
以下是相关代码:
// src/components/Iframe.js
import React, { useRef, useEffect, useState } from 'react'
import { createPortal } from 'react-dom'
import { CacheProvider } from '@emotion/core'
import createCache from '@emotion/cache'
import weakMemoize from '@emotion/weak-memoize'
// literally copied from Mitchell's codesandbox
// https://github.com/emotion-js/emotion/issues/760#issuecomment-404353706
let memoizedCreateCacheWithContainer = weakMemoize(container => {
let newCache = createCache({ container });
return newCache;
});
/* render Emotion style to iframe's head element */
function EmotionProvider({ children, $head }) {
return (
<CacheProvider value={memoizedCreateCacheWithContainer($head)}>
{children}
</CacheProvider>
)
}
/* hack-ish: force iframe to update */
function useForceUpdate(){
const [_, setValue] = useState()
return () => setValue(0)
}
/* rudimentary Iframe component with Portal */
export function Iframe({ children, ...props }) {
const iFrameRef = useRef(null)
const [$iFrameBody, setIframeBody] = useState(null)
const [$iFrameHead, setIframeHead] = useState(null)
const forceUpdate = useForceUpdate()
useEffect(function(){
if (!iFrameRef.current) return
const $iframe = iFrameRef.current
$iframe.addEventListener('load', onLoad)
function onLoad() {
// TODO can probably attach these to ref itself?
setIframeBody($iframe.contentDocument.body)
setIframeHead($iframe.contentDocument.head)
// force update, otherwise portal children won't show up
forceUpdate()
}
return function() {
// eslint-disable-next-line no-restricted-globals
$iframe.removeEventListener('load', onload)
}
})
return (<iframe {...props} title="s" ref={iFrameRef}>
{$iFrameBody && $iFrameHead && createPortal((
<EmotionProvider $head={$iFrameHead}>{children}</EmotionProvider>
), $iFrameBody)}
</iframe>)
}
如果您希望在 gatsby build 期间预渲染 iFrame,则需要做更多工作。
对于styled-components 用户,我发现Stephen Haney 的这个sn-p 看起来比emotion 优雅得多:
[...] styled-components 包含一个 StyleSheetManager 组件
可以带一个目标道具。目标需要一个 DOM 节点,它会
将其动态创建的样式表附加到该节点。
react-frame-component 使用 React 的新版 Context API 来
公开FrameContextProvider。它包括IFrame 文档和
上下文中的窗口。
您可以将这两个API组合如下使用styled-components
在您的 IFrame 中:
{
frameContext => (
<StyleSheetManager target={frameContext.document.head}>
<React.Fragment>
{/* your children here */}
</React.Fragment>
</StyleSheetManager>
)
} </FrameContextConsumer> </Frame>
这与 react v16.4.1、styled-components v3.3.3 和 react-frame-component v4.0.0 完美配合。