Я думаю, что проблема здесь в применении стиля, созданного emotion
, к кнопке, размещенной внутри iframe.
Я нашел этот отличный пример от Митчелла (основная группа эмоций), который делает именно то, что вам нужно: github
Вот вилка вашего кодаcodeandbox с скопированным кодом с рудиментарным самодельным элементом <Iframe>
: codesandbox < / а>
![результат](https://i.stack.imgur.com/yXVn5.png)
Вот соответствующий код:
// 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>)
}
Это требует дополнительной работы, если вы хотите, чтобы ваши iFrames были предварительно обработаны во время gatsby build
.
Для styled-components
пользователей я нашел этот фрагмент от Стивена Хейни, который выглядит намного элегантнее. чем emotion
:
[...] styled-components
включает компонент StyleSheetManager, который может принимать целевую опору. Цель ожидает узел DOM и будет прикреплять свои динамически созданные таблицы стилей к этому узлу.
react-frame-component
использует новую версию API контекста React для отображения FrameContextProvider
. Он включает IFrame
документ и окно в контексте.
Вы можете объединить эти два API следующим образом, чтобы использовать styled-components
внутри своих IFrames:
{
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.
person
Derek Nguyen
schedule
02.12.2019