如何设置React组件的iframe内容
我正在尝试在React组件中设置iframe的内容,但是我无法做到这一点。我有一个组件,其中包含一个在iframe完成加载时必须调用的函数。在该函数中,我正在设置内容,但似乎根本没有调用onload函数。我正在Chrome浏览器中对其进行测试。我正在尝试以下方法:
var MyIframe = React.createClass({ componentDidMount : function(){
var iframe = this.refs.iframe.getDOMNode();
if(iframe.attachEvent){
iframe.attacheEvent("onload", this.props.onLoad);
}else{
iframe.onload = this.props.onLoad;
}
},
render: function(){
return <iframe ref="iframe" {...this.props}/>;
}
});
var Display = React.createClass({
getInitialState : function(){
return {
oasData : ""
};
},
iframeOnLoad : function(){
var iframe = this.refs.bannerIframe;
iframe.contentDocument.open();
iframe.contentDocument.write(['<head></head><style>body {margin: 0; overflow: hidden;display:inline-block;} html{ margin: 0 auto; text-align: center;} body > a > img {max-width: 100%; height: inherit;}', extraCss, '</style></head><body>', this.state.oasData.Ad[0].Text, '</body>'].join(''));
iframe.contentDocument.close();
},
setOasData : function(data){
this.setState({
oasData : JSON.parse(data)
});
},
componentDidMount : function(){
var url = "getJsonDataUrl";
var xhttp = new XMLHttpRequest();
var changeOasDataFunction = this.setOasData;
xhttp.onreadystatechange = function () {
if (xhttp.readyState == 4 && xhttp.status == 200) {
changeOasDataFunction(xhttp.responseText);
}
};
xhttp.open("GET", url, true);
xhttp.send();
},
render : function(){
return (
<MyIframe refs="bannerIframe" onLoad={this.iframeOnLoad} />
);
}
});
module.exports = Display;
我究竟做错了什么?
回答:
随着React
16中Portal的引入,整个框架实际上变得微不足道了。iframe内容不仅实现和用法更加简单明了,而且还是“父”虚拟dom的实际子元素,这意味着共享事件系统,上下文等。太好了吧?
import React, { Component } from 'react'import { createPortal } from 'react-dom'
export default class Frame extends Component {
constructor(props) {
super(props)
this.setContentRef = node =>
(this.contentRef =
((!node || !node.contentWindow) && null) ||
node.contentWindow.document.body)
}
render() {
const { children, ...props } = this.props // eslint-disable-line
return (
<iframe {...props} ref={this.setContentRef}>
{this.contentRef &&
createPortal(
React.Children.only(children),
this.contentRef
)}
</iframe>
)
}
}
使用React Hooks时,它变得更加简洁:
import React, { useState } from 'react'import { createPortal } from 'react-dom'
export const IFrame = ({ children, ...props }) => {
const [contentRef, setContentRef] = useState(null)
const mountNode = contentRef && contentRef.contentWindow.document.body
return (
<iframe {...props} ref={setContentRef}>
{mountNode &&
createPortal(
React.Children.only(children),
mountNode
)}
</iframe>
)
}
用法:
import Frame from './Frame'const MyComp = () => <Frame><h1>Hello Content!</h1></Frame>
<head>
如Gist所示,可以轻松实现对iframe的进一步控制。
还有一个react-frame-component,一个包,恕我直言,IMHO提供了在React中使用iframe时所需的几乎所有东西。
服务器端渲染(至少据我所知)是服务器端渲染的一件事(至少据我所知),因为门户仅在引用实际DOM节点时才可渲染。所以,如果你…
- …迫切需要预渲染您的iframe内容
- …别介意切割虚拟dom树
- … 支持iframe属性的目标浏览器
srcdoc
…那么您可以从以下内容开始:
import React, { Component } from 'react'import { renderToString } from 'react-dom/server'
import { hydrate, render } from 'react-dom'
const wrapWithMountNode = html => {
return `<!DOCTYPE html><html><head></head><body><div id="frame">${html}</div></body></html>`.trim()
}
export default class SSRFrame extends Component {
constructor(props) {
super(props)
this.initialMarkup = wrapWithMountNode(
renderToString(
React.Children.only(this.props.children)
)
)
this.contentRef = null
this.setContentRef = node => {
this.contentRef =
((!node || !node.contentWindow) && null) ||
node.contentWindow.document.getElementById('frame')
}
}
componentDidMount() {
this.contentRef &&
hydrate(
React.Children.only(this.props.children),
this.contentRef
)
}
componentDidUpdate() {
this.contentRef &&
render(
React.Children.only(this.props.children),
this.contentRef
)
}
componentWillUnmount() {
this.contentRef = null
}
render() {
const { children, ...props } = this.props // eslint-disable-line
return (
<iframe
{...props}
ref={this.setContentRef}
srcDoc={
(!this.contentRef && this.initialMarkup) ||
undefined
}
/>
)
}
}
2018年快乐取景!
就IFrame而言,解决方案实际上非常简单:您必须为IFrame内容创建一个新的DOM渲染器,并将其与应用程序的其余部分同步。这样的组件可能看起来像这样:
var React = require('react');var ReactDOM = require('react-dom');
var IFrame = React.createClass({
propTypes = {
frameProps: React.PropTypes.object,
},
defaultProps = {
frameProps: {
frameBorder: 0
}
},
updateIFrameContents: function() {
ReactDOM.render((
// Here you put the components that should be in the IFrame
<div {...this.props}>Hello IFrame!</div>
), this.el);
},
render: function() {
return (
<iframe {...this.props.frameProps} />
);
},
componentDidMount: function() {
var frameBody = ReactDOM.findDOMNode(this).contentDocument.body,
el = document.createElement('div');
frameBody.appendChild(el);
this.el = el;
this.updateIFrameContents();
},
componentDidUpdate: function() {
this.updateIFrameContents();
}
});
现在,这不是很友好的构图。您不能使用React.props.children.only
等,因为它们总是指向已经作为差异树一部分的已编译/创建的元素。并且由于我们要为框架内容创建一个新的差异树,因此您必须为每个框架内容定义一个新组件。
输入高阶组件。我们的目标是创建一种装饰器,该装饰器可以应用于您想要构建的任何元素:
function FramedComponent(Component) { return React.createClass({
propTypes = {
frameProps: React.PropTypes.object,
},
defaultProps = {
frameProps: {
frameBorder: 0
}
},
updateIFrameContents: function() {
ReactDOM.render((
<Component {...this.props} />
), this.el);
},
render: function() {
return (
<iframe {...this.props.frameProps} />
);
},
componentDidMount: function() {
var frameBody = ReactDOM.findDOMNode(this).contentDocument.body,
el = document.createElement('div');
frameBody.appendChild(el);
this.el = el;
this.updateIFrameContents();
},
componentDidUpdate: function() {
this.updateIFrameContents();
}
});
}
像这样使用:
var MyFramedComponent = FramedComponent(MyComponent);
React可能比它目前流行的所有其他虚拟DOM库要
另一个主要优点就是它的综合事件系统。很好的法律,它只是工作,并且非常方便地冒泡。
但是,通过这种方法,您将(有意地)从下一棵差异树中删除一棵,并且对于事件系统也是如此。对于大多数没有太大区别的事件:
var logNestedClicks = function(event) { console.log(event);
}
// and then
<MyFramedComponent onClick={logNestedClicks) />
这样就可以了。但是有一些不太明显的例外,尤其是考虑到React中的受控iFrame经常不仅仅用于创建作用域这一事实,它不能按预期工作。另一个不太突出的示例:onBeforeInsert
。这使得对草稿实例进行范围界定非常繁琐。再说一次,这对于大多数用例来说可能是无关紧要的。在为React中的iFrame制作(所见即所得)案例之前,只需确保您的狗屎能按预期的方式捕获即可。到那里去,做到了,相信我。
以上是 如何设置React组件的iframe内容 的全部内容, 来源链接: utcz.com/qa/431136.html