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

Oddly Behaving Conditional Rendering on First Load #15993

Closed
AllanPooley opened this issue Jul 23, 2019 · 6 comments
Closed

Oddly Behaving Conditional Rendering on First Load #15993

AllanPooley opened this issue Jul 23, 2019 · 6 comments

Comments

@AllanPooley
Copy link
Contributor

AllanPooley commented Jul 23, 2019

Description

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.

Expected result

My expected result is the specified component renders depending on the window.innerWidth, as seen in these screenshots:

Mobile

image
image

Actual result

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:

image
image

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

image

Though, after I hard refresh the page, everything is back to normal, working as intended.

Steps to reproduce

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:

https://www.carbon8.org.au/

  • Load the page for the first time
  • To recreate, clear your cookies, cache, deregister service worker and load again.

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:

image

Environment

  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
@AllanPooley
Copy link
Contributor Author

Possibly related to #11023 and "React hydration"?

@AllanPooley
Copy link
Contributor Author

AllanPooley commented Jul 23, 2019

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.

@mgrpowers
Copy link

@AllanPooley, I'm having the exact same issue. Have you learned anything new recently? I would prefer to not render both components.

@AllanPooley
Copy link
Contributor Author

@mgrpowers unfortunately I ended up refactoring all of my code that used this logic in my own case.

As mentioned earlier, there’s little that can be done to work around it because of the mechanism of React Hydration.

Let me know if you discover otherwise

@dzuncoi
Copy link

dzuncoi commented Sep 5, 2019

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 :(

@AllanPooley
Copy link
Contributor Author

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.

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

No branches or pull requests

3 participants