Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fragments in IE11 rendered as strange elements #13414

Closed
jamiewinder opened this issue Aug 16, 2018 · 19 comments
Closed

Fragments in IE11 rendered as strange elements #13414

jamiewinder opened this issue Aug 16, 2018 · 19 comments

Comments

@jamiewinder
Copy link

jamiewinder commented Aug 16, 2018

React / ReactDOM: 16.4.2

I'm having trouble reproducing this, but I'm raising it in the hope that someone can give me some clues as to the cause. I have a large application which is using Fragment in a few places, like so:

<React.Fragment>
    <div>Child 1-1</div>
    <div>Child 1-2</div>
</React.Fragment>
<React.Fragment>
    <div>Child 2-1</div>
    <div>Child 2-2</div>
</React.Fragment>

In everything but Internet Explorer, this renders as you'd expect:

<div>Child 1-1</div>
<div>Child 1-2</div>
<div>Child 2-1</div>
<div>Child 2-2</div>

However, for some reason in Internet Explorer 11, these are being rendered as some weird tags:

<jscomp_symbol_react.fragment16>
    <div>Child 1-1</div>
    <div>Child 1-2</div>
</jscomp_symbol_react.fragment16>
<jscomp_symbol_react.fragment16>
    <div>Child 2-1</div>
    <div>Child 2-2</div>
</jscomp_symbol_react.fragment16>

I've tried pausing the code at the transpiled _react.createElement(_react.Fragment) line, and the _react.Fragment export is a string with the same name as the tag (jscomp_symbol_react.fragment16). I think this is just the correct way in which the symbol polyfill works and that React should recognize it as something other than an HTML tag.

What's even weirder is that this only happens sometimes. If two components in my app are using fragments, the first one to render may have the above issue, the second may not. If an affected component re-renders, the rendered DOM will be corrected. I haven't found a solid pattern to this yet.

I have a fairly typical webpack + babel setup, and using the babel-polyfill for symbol support. I'm really not sure what parts of my setup are relevant to this so please let me know if you need any extra info. Again, I'm trying to create a reproduction outside of my application but if anyone can offer me some clues in the meantime I'd be incredibly grateful.

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

Is it possible that you load babel-polyfill between react and react-dom is loaded, so one of them thinks Symbol is supported, but the other one thinks Symbol isn't supported? Then they would get out of sync which can lead to confusing behavior.

See #8379 (comment) for a similar problem. You can search for typeof Symbol checks in both react and react-dom bundles, and put some logs there to verify if they “agree” whether Symbol exists or not.

screen shot 2018-08-16 at 1 45 52 pm

screen shot 2018-08-16 at 1 46 00 pm

@jamiewinder
Copy link
Author

jamiewinder commented Aug 16, 2018

Thanks for your reply.

I'm getting somewhere with this now. After deconstructing my app bit-by-bit, the surprising cause of this appears to be... Google Maps!

I'm loading the Google Maps JavaScript API with a script tag, then loading my bundled app with another script tab. If I reverse these, it works.

It looks like the Google Maps API includes a Symbol polyfill of its own which is somehow confusing things. I'm still none the wiser as to why though as after looking at the lines of code you suggested I'm even more confused...

There are three instances that I can see where React libraries (react, react-dom, and react-is) are checking for Symbol.for, and all three say it does exist and appear to point to the same function which, as far as I can tell, is the core-js implementation, not Google Maps':

Symbol.for	function (key) { return has(SymbolRegistry, key += '') ? SymbolRegistry[key] : SymbolRegistry[key] = $Symbol(key); }

So irrespective of whether Google Maps is loaded first, last, or not at all the bundled application and all of the React libraries appear to use the same core-js polyfill of Symbol. So why this is an issue at all is a mystery to me, as is why loading Google Maps first has any influence over it.

Also, since you mentioned it, and just in case the Google Maps thing is a red herring, I'm pretty sure I'm loading babel-polyfill before everything else by having the following in my webpack config:

    entry: [
        'babel-polyfill',
        path.resolve(__dirname, 'src/index.js') /* my entry point */
    ]

As far as I'm aware, this will load the polyfill before anything else.

Thanks again.

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

If you can reduce it to a reproducible example (now that you know it has something to do with Google Maps) I can take a look.

@jamiewinder
Copy link
Author

jamiewinder commented Aug 16, 2018

Thanks! I think I've got it down to its bare bones here:
https://github.com/jamiewinder/ie11-fragment-issue

You should be able to just run yarn run build-run. If you look at the DOM in Internet Explorer 11 you should see the weird tag. If you click the Update button on the component (which re-renders it), the tag then goes away.

If you then reverse the script tags in index.html (so Google Maps is loaded last) then the weird tag never appears in the first place!

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

Thanks. I won't have time to dig into this in close future but I'll leave this for somebody motivated.

@Haroenv
Copy link
Contributor

Haroenv commented Aug 16, 2018

have dealt with this same issue in InstantSearch.js (we were using preact), and the fix was this: preactjs/preact-compat#423

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

We do check for for though.

@sanniassin
Copy link

sanniassin commented Aug 16, 2018

The problem is that core-js polyfill for Symbol.for uses global Symbol which doesn't get polyfilled because it is already defined by gmaps. However, maps' implementation of Symbol returns string while correct implementations return actual symbol or object, so it's possible to check if Symbol returns value of valid type. It's quite hacky though and I'm not sure whether this check should be implemented in core-js or React.

@Shenlok
Copy link

Shenlok commented Aug 16, 2018

As @sanniassin pointed out, the Google Maps polyfill is causing Symbol() to return strings. There are various places in the React code that check the typeof an element's type, and often checking for string types before checking for the REACT_FRAGMENT_TYPE 'Symbol' e.g here

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

the Google Maps polyfill is causing Symbol() to return strings

Jeez. Okay. Can we report it to them?

@kabbany
Copy link

kabbany commented Aug 16, 2018

I had the same issue before and I solved it by fixing the version of Google Maps
<script src="https://maps.googleapis.com/maps/api/js/33/2?v=3"></script>
I tried it in your example and it is working fine.

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

Do you mean a newer version fixes it? Or an older one?

@kabbany
Copy link

kabbany commented Aug 16, 2018

Unfortunately older one

@Shenlok
Copy link

Shenlok commented Aug 16, 2018

Seems to be an issue with the Symbol polyfill implementation from the Closure compiler, see Sam Saccone's tweet

@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

I filed google/closure-compiler#3052. I don't think we'll be adding more "bad Symbol polyfill" checks — IMO something that GCC should fix.

@gaearon gaearon closed this as completed Aug 16, 2018
@gaearon
Copy link
Collaborator

gaearon commented Aug 16, 2018

According to google/closure-compiler#3052 (comment), one workaround would be to ensure that core-js code (which you run via babel-polyfill) executes earlier than the Google Maps bundle. I guess you could load the Google Maps API asynchronously (and not assume it's loaded yet in your app code) by creating a dynamic script tag and hooking into its onload event.

@jamiewinder
Copy link
Author

Thanks, that's what I ended up doing. My bundled app is the first script to run, so core-js gets to install its polyfill first.

Adding the Google Maps library is also now done with the load-google-maps-api library to ensure it's done within the polyfilled app but before the parts of the app that use it. Works well.

Thanks for all of your help with this.

jasmussen pushed a commit to WordPress/gutenberg that referenced this issue Nov 20, 2018
This is an exotic one.

In IE11, in the toolbar markup, you can briefly see the appearance of a new HTML element called `<jscomp_symbol_react.fragment16>`. This is obviously not a real element, but it is rendered by IE11 as a real element, which means it breaks the flexing of the actual children of the toolbar.

This PR _styles_ that temporary element, to at least not be broken.

This is an ugly workaround to an upstream React issue reported here: facebook/react#13414
@drpiou
Copy link

drpiou commented Feb 1, 2019

Many thanks to all. I'm using ReCaptcha from Google and I had the same. When I moved the recaptcha script call AFTER my bundle.js, everything went just fine.

@AngelWeed
Copy link

  • [ ]
  • _
    Duplicate of #_

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

8 participants