我试图在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 ; } }); var Display = React.createClass({ getInitialState : function(){ return { oasData : "" }; }, iframeOnLoad : function(){ var iframe = this.refs.bannerIframe; iframe.contentDocument.open(); iframe.contentDocument.write(['', this.state.oasData.Ad[0].Text, ''].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 (); } }); module.exports = Display;
我究竟做错了什么?
编辑10-25-2018
在React 16中引入门户网站,整个框架的内容实际上变得微不足道.不仅实现和使用更直接,iframe内容也是«parent»虚拟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 ( ) } }
使用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 ( ) }
用法:
import Frame from './Frame' const MyComp = () =>Hello Content!
进一步的控制,例如在不同iframe ,可以容易地实现作为本主旨所示.
还有react-frame-component,一个包,恕我直言提供了在React中使用iframe时所需的一切.
SSR
有一件事(至少据我所知),你几乎无法用Portal实现,是服务器端渲染,因为当它们具有对实际DOM节点的引用时,它们只能被渲染.所以,如果你......
...迫切需要预渲染的iframe内容
...不介意切割虚拟dom树
...目标浏览器,支持srcdoc
iframe上的属性
...然后你可以从这样的事情开始:
import React, { Component } from 'react' import { renderToString } from 'react-dom/server' import { hydrate, render } from 'react-dom' const wrapWithMountNode = html => { return `${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 ( ) } }
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 IFrameHello IFrame!), this.el); }, render: function() { return ( ); }, 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
和喜欢,因为这些总是指向已经编译/创建的元素,这些元素已经是diff树的一部分.而且因为我们想要一个新的框架内容的差异树,你必须为每个框架内容定义一个新的组件.
输入高阶订单组件.目标是创建一种装饰器,可以应用于您想要框架的任何元素:
function FramedComponent(Component) { return React.createClass({ propTypes = { frameProps: React.PropTypes.object, }, defaultProps = { frameProps: { frameBorder: 0 } }, updateIFrameContents: function() { ReactDOM.render((), this.el); }, render: function() { return ( ); }, 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
这样做会很好.但是有一些不那么突出的例外,特别是考虑到React中的受控iFrame通常不仅仅用于创建范围这一事实,只是没有按预期工作.一个又一个不那么突出的例子:onBeforeInsert
.这使得范围草案实例成为一项非常繁琐的任务.然后,这可能与大多数用例无关.在React中为iFrames制作(WYSIWYG)案例之前,请确保以您期望的方式捕获您的狗屎.去过那里,做到了,相信我.
如果有人只想在iframe中显示小的HTML,则有一个更简单的解决方案。
内容的最大长度为32768个字符。
还有一个易于使用的react-frame-component软件包,该软件包在已接受的答案中已提到。