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

Code Split Mobile and Desktop Component Trees using React.Lazy + Suspense #3

Closed
joealden opened this issue Nov 14, 2018 · 2 comments
Closed
Labels
upstream The issue is dependent on an upstream issue

Comments

@joealden
Copy link
Owner

Idea

Instead of using a load of CSS media queries and React media queries (likely using the react-media library), somewhere near the root of the component tree (probably just below the theme provider as both mobile and desktop will use it), a single React media query is used. This media query (still probably using react-media unless React Hooks are stable by the time this is implemented) will render the <Desktop /> component if the viewport width is >= 1024px, or it will render the <Mobile /> component if the viewport is < 1024px. This media query will be wrapped in a <React.Suspense /> component so that both the <Desktop /> and <Mobile /> components can be imported using React.lazy.

Inspiration: https://twitter.com/necolas/status/1058949410128707584

Benefits

Separating the desktop and mobile designs into their own lazy loaded component trees has the following benefits:

  • The amount of code the user downloads will be reduced if they only render one of the two component trees (which will be the most common case). For example, if the user loads the page on desktop and doesn't resize the browser to a mobile size, they will only download the code required for the desktop version of the site.
  • As styled-components is used for component level CSS, the user will also only download the CSS they need for the current breakpoint. Without this code-splitting mechanism in place, the user would download all media query data even when it likely won't be used.
  • Components that are designed and built just for desktop or just for mobile will be a lot simpler to write and read. For example, a component that is only rendered on the desktop does not need to have loads of conditionals and CSS media queries, unlike a generic component that would be rendered both on desktop and on mobile.
  • Code that can be shared across desktop and mobile can still be shared through JavaScript modules (This includes JavaScript [regular functions], HTML [components] and CSS [styled components]).

Potential Issues

  • As mentioned here in the React docs, React.lazy and Suspense are not yet available for server-side rendering. As Gatsby statically server-side renders the site at build time, this may be an issue. I'll have to play around with it in Gatsby and see what happens. As also mentioned in the react docs section linked above, react-loadable may have to be used as they do support server-side rendering.
  • If React.lazy and Suspense are used, then I need to check how 'fetch on render' lazy loading actually works. From what I can tell, the proposed way of wrapping the media query in a Suspense component should work, but I need to make sure that only the needed component tree is fetched instead of both of them.
@joealden joealden added the idea An idea that may or may not be useful label Nov 14, 2018
@joealden
Copy link
Owner Author

I have tried using React.Suspense and React.Lazy to code split the mobile and desktop component trees, however, when building the site for production using gatsby build, the following error is printed to the console:

ReactDOMServer does not yet support Suspense.

This means that my concerns about Gatsby and Suspense were true. You cannot currently use Suspense with Gatsby. This will likely be the case until the React team bring out an SSR compatible version of Suspense. After this, Gatsby may also need to make some changes, but it is possible that it will not be required. They may only have to bump the version of ReactDOMServer used to the version that supports SSR suspense.

I then tried to use react-loadable as suggested in the React docs if the developer needs code splitting that supports SSR. However, from reading gatsbyjs/gatsby#5995, it seems as though it is also currently not possible to use react-loadable in Gatsby.

I will continue splitting the desktop and mobile component trees, but for now, both trees will be loaded no matter which one is actually shown, which is a shame. However, the mobile component tree shouldn't be too big as it should be a lot simpler to implement than the desktop component tree, and there will also be places where code can be shared across the trees.

For the time being, I will mark this as an upstream issue. This issue should be revisited when either Gatsby gets support for react-loadable or when the React team release an SSR compatible version of Suspense. Note that it is probably unlikely that the Gatsby team with implement react-loadable support into the core when they know that SSR Suspense is coming (hopefully) in the near future.

@joealden joealden added upstream The issue is dependent on an upstream issue and removed idea An idea that may or may not be useful labels Nov 23, 2018
@joealden
Copy link
Owner Author

Due to the fact that the MediaQuery component doesn't get rendered server side (as it renders conditionally dependent on the user's viewport width), you can place a React.Suspense component around both the mobile and desktop component trees by placing the component just inside each of the MediaQuery components.

This meant that Suspense was working, as the components are never rendered on the server (which at the moment isn't possible, see the comment above). From there, I replaced the statically imported MobileSite and DesktopSite components with dynamically imported versions (using React.lazy).

I then created a production build with these changes and compared them to the old version to see if there was much of a payload size reduction. From what I can see, it only cut's off about 10kb, and actually increases the TTI (Time To Interactive) as the dynamic imports are only fetched once they are needed in the shared code.

This means that it just isn't worth using Suspense with Listed's split component tree architecture, as the payload size reduction is not worth the increased TTI. It may have been more useful if the site had many pages, where each page had a mobile and desktop component tree, as this would have likely made it so that client side route navigation was faster.

For the above reason, I will close this issue.

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

No branches or pull requests

1 participant