krakenjs / zoid

Cross domain components
Apache License 2.0
2.01k stars 248 forks source link

Loading spinner stays visible while iframe content is invisible, even after load #136

Closed seans887 closed 5 years ago

seans887 commented 6 years ago

XComponent version: 6.0.35 React version: 15.5.4 Node version: v8.8.1

Expectation: Once the iframe content has been loaded from its URL, the default xcomponent loading spinner should disappear and only the iframe content should remain. What's happening: When I load my xcomponent on the page, the loading spinner stays there forever and never displays the actual iframe content. This is despite the fact that the content has already loaded, which I confirmed by manually changing the CSS class of the loading spinner to hide it, while also changing the CSS class of the content to show it.

screen shot 2018-01-09 at 4 58 07 pm

Note: The JS error notification you see on the right is for 1) a warning that React 16 will deprecate React.createClass, which apparently is being used by xcomponent, and 2) a 404 on an unrelated GET request. Neither should be influencing the iframe load, to my knowledge.

Description: I'm trying to render an xcomponent into a React application. For now, the iframe on the other side is simply a static HTML file with an <h1> in it. After reading the docs, I created the following component:

// components/AddCardFrame.js

import xcomponent from 'xcomponent';
import React from 'react';
import ReactDOM from 'react-dom';

const component = xcomponent.create({
    tag: 'add-card-frame',
    url: 'http://localhost:4321'
});

let AddCardFrame = component.driver('react', {
    React: React,
    ReactDOM: ReactDOM
});

export default AddCardFrame;

I'm using the React drivers to generate my component, which I am using elsewhere very simply:

// index.js
...
import AddCardFrame from '../components/AddCardFrame';
...
class Footer extends Component {
...
      render() {
         ...
         <AddCardFrame />
      }
}

After digging through the xcomponent source a bit, it seems as though the switchPrerender method may be the culprit somehow (not being triggered?), as it seems to be responsible for switching between the prerendered content and the actual content on load. Beyond that I'm pretty stuck though. Can provide more details as needed. Thanks!

seans887 commented 6 years ago

We've figured this one out - it turned out we were not requiring the original xcomponent instance inside of the child iframe. I didn't see this noted in the docs or the Medium article anywhere so we didn't add it - but eventually a coworker noticed in the example React code that in the child iframe they had imported xcomponent and the original xcomponent instance (in the demo code this was login.js) in the child iframe as well as the parent. I'm assuming this is necessary to create the post-robot messaging bridge on both sides?

It would be ideal if it could be noted in the documentation that it's necessary to require both xcomponent and the original xcomponent instance in the child iframe. Thank you.

bluepnume commented 6 years ago

Ah fantastic, glad you figured out the problem. I'll keep this open as a reminder to update the docs -- unless you'd like to make a PR with a note about this.

Thanks!

ar7casper commented 6 years ago

@seans887 Any chance you can share your definition of the 'prerenderTemplate'?

francois-roget commented 5 years ago

@seans887 I am facing the exact same problem. Could you please provide a working example? Thanks in advance

muhaimincs commented 5 years ago

hi.. can you share the workaround to solve this. Thanks

espring commented 5 years ago

I have the same issue, and figured it out after I read @seans887's reply many times...

The key point is that not only the parent page but also the child iframe page should include the xcomponent /zoid definition files!

Take a look at the example code : zoid / demo / frameworks / react /

dchersey commented 4 years ago

Had the same problem, but with a twist ... I was trying to render a react.js component in a second iFrame in the parent window, initiated by a button in a react component that was also embedded in a different iFrame of the parent window.

So the following code in the parent page:

const ZoidComponent1 = zoid.create({
  tag: 'zoid1',
  url: 'http://localhost:3000/component1'
}

const ZoidComponent2 = zoid.create({
  tag: 'zoid2',
  url: 'http://localhost:3000/component2'
}

const ReactComponent1 = ZoidComponent1.driver('react', {
  React,
  ReactDOM
});

function App() {
return (
    <div className="App">
        <ReactComponent1 />
        <div id="container2"/>
    </div>);
}

renders the first component just fine.

In ReactComponent2 I have

const ZoidWidget1 = zoid.create({
  tag: 'zoid1',
  url: 'http://localhost:3000/component1'
}

const ZoidWidget2 = zoid.create({
  tag: 'zoid2',
  url: 'http://localhost:3000/component2'
}

onButtonClick () {
    ZoidComponent2.renderTo(window.parent, {}, "#container2");
}

This rendered just fine into #container2 on the parent, but kept on spinning. The event is never called to swap the visble/invisible classes in the zoid frame.

I finally figured out that my ReactComponent2 needs to include the definition of ZoidComponent2 ... but so does ReactComponent1 so it can reference it in the above code.

When I include it in Both components, I get Error: Can not register multiple components with the same tag.
When I only include it in ReactComponent1, the spinner keeps loading, because ReactComponent2 does not include it.
When I only include it in ReactComponent1, I can't call renderTo for the component because ZoidComponent1 is not defined.

Is there a way to render it using the tag? Like zoid.render('zoid2', window.parent, {}, '#container2')?

I am working around this by embedding both components in the parent page as React Components using the react driver, then using a callback through zoid to the parent to cause ReactComponent2 to render, based on state change.

But this feels unnecessarily complex for the hosting page ... would love to find a way to trigger this from inside ReactComponent1.