I'm using MobX 5 in my Gatsby app, which requires proxy support and thus it'll simply throw an error and crash on IE (all versions). I don't care at all about IE (nor Edge actually), but I still don't want my users to see an empty page when they try to access my app from one of those browsers.
I've tried a couple of different approaches, but they all seemed hacky and unpleasant.
What's the best way to display a message for these users ? I reckon it has to be done at the lowest (or earliest) stage possible, as MobX will crash JS right after it's been imported (and I import in in my layout component of course).
I considered using conditional HTML comments in my html.js file ([If !IE]...), and serve a specific bundle to those users, but that proved to be quite challenging because React doesn't really allow this in a simple fashion.
I have a feeling the key is in html.js but I can't quite figure it out. I'm pretty sure other people wanted to achieve something similar, any tips ?
Maybe just using proxy polyfill would be easy way out of it - something like https://github.com/GoogleChrome/proxy-polyfill?
As for [If !IE] you would probably need to adjust html.js as you suspected and use something like https://zhenyong.github.io/react/tips/dangerously-set-inner-html.html so react just render strings there. Not sure if this will really work, but worth a try I think
Hey, thanks for taking the time to take a look at this.
However, Google's polyfill doesn't support all of proxy's traps and unfortunately won't help me. I still have the option to downgrade MobX, but since I don't really care about IE I chose the "display a message" route. But yeah, ideally, a polyfill would have been what I would have chosen.
As for HTML comments, React requires a wrapper to use dangerouslySetInnerHTML (like a div, a span, etc..), which doesn't really cut it for me.
I have researched this a little further since posting this issue, but no good way to do this came up.
This is why I geared this question towards "how do I detect browser early on, and chose what top component to render in Gatsby", if this makes any sense ? Ideally, I'd like to be able to run code in the browser as early as html.js, right as it mounts and implement my logic here.
@Coriou what about just adding a custom script? e.g. in html.js something like the below--consider this pseudocode. Not 100% sure it will work but worth a shot. The general approach would be:
function Html(props) {
return (
<html {...props.htmlAttributes}>
<head>
<meta charSet="utf-8" />
<meta httpEquiv="x-ua-compatible" content="ie=edge" />
<meta
name="viewport"
content="width=device-width, initial-scale=1, shrink-to-fit=no"
/>
<script dangerouslySetInnerHTML={{ __html: `
// note: this needs to go before any JS is loaded, e.g. probably in the head tags?
if (typeof window !== 'undefined' && typeof window.Proxy === 'undefined') {
// do something, e.g.
window.location.replace('/ie.html') // this presumes you set up an ie.html fallback, i.e. a vanilla HTML file you create yourself
}
` }} />
{props.headComponents}
</head>
<body {...props.bodyAttributes}>
{props.preBodyComponents}
<div
key={`body`}
id="___gatsby"
dangerouslySetInnerHTML={{ __html: props.body }}
/>
{props.postBodyComponents}
</body>
</html>
)
}
export default Html
@pieh also recommended using onClientEntry. This is a great place to test out the above script and see if it works for you!
Damn it. Yes, that鈥檚 it guys. Just gotta redirect manually to a dummy page using the onClientEntry hook. As simple as that.
Thank you very much to both of you !
Hey. Just a little follow up on this issue :
I found out that onClientEntry isn't guaranteed to run first, using this hook didn't help me because html.js was already rendered by the time I ran my code and the JS was crashed on IE. Maybe I'm doing something wrong here, but it seemed pretty straight forward
I tried using usual logic in html.js to implement my own version of this hook : in the render method, I wait for window to be available (by "blocking" the rendering of the page until it is). When I becomes available, I do my user agent checking to determine whether it's IE or not. When that check is done, I either redirect to my IE specific page or allow the rendering of the real page. This approach didn't work because for some reason the html.js doesn't seem to behave like a regular React component (state update doesn't trigger a re-render and componentDidMount is never called)
I placed a lot of faith in the second approach. It should by all means work if the html.js was behaving as a regular React component, but it seems that I can not trigger a re-render at any point... I didn't dive deep into Gatsby's core code, but it seems that html.js is only processed server-side, never in the browser.
I feel like I'm closer to solving this, but I'm still missing something here ...
@Coriou is there a reason the approach I posted won't work? It seems pretty clear that that snippet will execute before any other JS executes, and will therefore work exactly like you want?
I just tested it out, and it it seems like that script executes like you'd want and works appropriately.
@DSchau I鈥檒l give this exact approach a try tomorrow, you鈥檙e right it might just work... sorry for wasting your time. I鈥檒l let you know
Indeed, it just works... Thanks a lot !
@Coriou, @DSchau Just wondering how to create a page outside of Gatsby, as recommended above? I have a similar issue.
@chocobuckle You can generate it using the build hooks of Gatsby. In my case, I created the HTML page and using the onPostBootstrap hook to write it to the public directory on build:
// gatsby-node.js
exports.onPostBootstrap = () => {
let iePage = path.resolve("./src/ie.html"),
iePublicPath = path.resolve("./public/ie.html")
if (fs.existsSync(iePage))
fs.createReadStream(iePage).pipe(fs.createWriteStream(iePublicPath))
}
Making the ie.html page available publicly at mysite.com/ie.html.
@Coriou Thanks for the explanation. All working perfectly.