Hi gang, in many of my recent projects I've employed conditional rendering to render seperate mobile and desktop versions of a component.
The way I achieve this is by adding the following state / logic to my components:
const SMALL_MOBILE_BREAKPOINT = 500;
const MOBILE_BREAKPOINT = 800;
const isClient = typeof window !== 'undefined'; // Prevents breaks when SSR
class Index extends Component {
state = {
viewportWidth: isClient ? window.innerWidth : 0,
};
componentDidMount() {
if (isClient) {
this.updateWindowDimensions();
window.addEventListener('resize', this.updateWindowDimensions);
}
}
componentWillUnmount() {
if (isClient) window.removeEventListener('resize', this.updateWindowDimensions);
}
updateWindowDimensions = () => {
this.setState({ viewportWidth: window.innerWidth });
}
const {
viewportWidth,
} = this.state;
const isSmallMobile = Boolean(viewportWidth <= SMALL_MOBILE_BREAKPOINT);
const isMobile = Boolean(viewportWidth <= MOBILE_BREAKPOINT);
render() {
isMobile ? (
<MobileComponent />
) : (
<DesktopComponent />
)
}
}
I'm seeing some really funky results when I first load the page.
My expected result is the specified component renders depending on the window.innerWidth, as seen in these screenshots:


What's really bizarre is that only on the first load of the page, the conditional rendering logic is not applied, components render in their desktop forms which cause a whole lotta issues:


This is happening despite the variables used in the logic correctly evaluating (see console logs):

Though, after I hard refresh the page, everything is back to normal, working as intended.
I'll try spin up better test environment within the next day, but for the time being I have the project I'm experiencing this issue on to share:
Additionally, here's an environment where added some console logs and replaced my own breakpoint logic with react-responsive:
https://deploy-preview-95--carbon8.netlify.com/
If you've got the Lighthouse Plugin an easy way to reproduce this (clear your cache, cookies, deregister) is just to run an audit:

System:
OS: macOS 10.14.5
CPU: (8) x64 Intel(R) Core(TM) i5-8259U CPU @ 2.30GHz
Shell: 3.2.57 - /bin/bash
Binaries:
Node: 11.14.0 - /usr/local/bin/node
Yarn: 1.15.2 - /usr/local/bin/yarn
npm: 6.7.0 - /usr/local/bin/npm
Browsers:
Chrome: 75.0.3770.142
Firefox: 66.0.5
Safari: 12.1.1
npmPackages:
gatsby: ^2.1.19 => 2.9.4
gatsby-image: ^2.0.30 => 2.1.4
gatsby-plugin-canonical-urls: ^2.0.12 => 2.0.13
gatsby-plugin-facebook-pixel: ^1.0.3 => 1.0.3
gatsby-plugin-google-analytics: ^2.0.19 => 2.0.21
gatsby-plugin-lodash: ^3.0.4 => 3.0.5
gatsby-plugin-mailchimp: ^5.1.0 => 5.1.0
gatsby-plugin-manifest: ^2.0.20 => 2.1.1
gatsby-plugin-netlify: ^2.0.11 => 2.0.17
gatsby-plugin-offline: ^2.0.24 => 2.1.3
gatsby-plugin-react-helmet: ^3.0.7 => 3.0.12
gatsby-plugin-robots-txt: ^1.4.0 => 1.4.0
gatsby-plugin-sass: ^2.0.11 => 2.0.11
gatsby-plugin-sharp: ^2.0.23 => 2.1.5
gatsby-plugin-sitemap: ^2.1.0 => 2.1.0
gatsby-plugin-stripe: ^1.2.1 => 1.2.1
gatsby-source-prismic: ^2.2.0 => 2.2.0
gatsby-source-stripe: ^3.0.1 => 3.0.1
gatsby-transformer-sharp: ^2.1.15 => 2.1.21
npmGlobalPackages:
gatsby-cli: 2.4.6
Possibly related to https://github.com/gatsbyjs/gatsby/issues/11023 and "React hydration"?
After reading up about React Hydration I can confidently conclude that this is what is causing my woes.
If you call ReactDOM.hydrate() on a node that already has this server-rendered markup, React will preserve it and only attach event handlers, allowing you to have a very performant first-load experience.
I think the only solution for me is to render both my Desktop and Mobile versions of the components and control which is displayed in my css.
@AllanPooley, I'm having the exact same issue. Have you learned anything new recently? I would prefer to not render both components.
@mgrpowers unfortunately I ended up refactoring all of my code that used this logic in my own case.
As mentioned earlier, there鈥檚 little that can be done to work around it because of the mechanism of React Hydration.
Let me know if you discover otherwise
Hi @AllanPooley, what approach do you use to handle refactoring your code? I'm encountering the same behaviour but no luck on trying in many ways :(
Hey @dzuncoi, my refactor involved reverted to rendering both the mobile version and the desktop version of the component and hiding the inactive component using CSS media queries.
Most helpful comment
Hey @dzuncoi, my refactor involved reverted to rendering both the mobile version and the desktop version of the component and hiding the inactive component using CSS media queries.