小编典典

如何设置React组件的iframe内容

reactjs

我正在尝试在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;

我究竟做错了什么?


阅读 1313

收藏
2020-07-22

共1个答案

小编典典

编辑10-25-2018

随着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节点时才可渲染。所以,如果你…

…那么您可以从以下内容开始:

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);

编辑3-7-2017 React可能比它目前流行的所有其他虚拟DOM库要 胜过的
另一个主要优点就是它的综合事件系统。很好的法律,它只是工作,并且非常方便地冒泡。

但是,通过这种方法,您将(有意地)从下一棵差异树中删除一棵,并且对于事件系统也是如此。对于大多数没有太大区别的事件:

var logNestedClicks = function(event) {
    console.log(event);
}
// and then
<MyFramedComponent onClick={logNestedClicks) />

这样就可以了。但是有一些不太明显的例外,尤其是考虑到React中的受控iFrame经常不仅仅用于创建作用域这一事实,它不能按预期工作。另一个不太突出的示例:onBeforeInsert。这使得对草稿实例进行范围界定非常繁琐。再说一次,这对于大多数用例来说可能是无关紧要的。在为React中的iFrame制作(所见即所得)案例之前,只需确保您的狗屎能按预期的方式捕获即可。到那里去,做到了,相信我

2020-07-22