如何设置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

回到顶部