Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

React/Redux isomorphic / server-side rendering and media queries

I started creating an isomorphic React/Redux app based on Node. One requirement of the project is "adapative" rendering of specific components based on a "mobile" and "desktop" view. I have implemented Redux action and reducer to store screen-information about the user's view (based on media queries - "small", "medium", "large") in the state. On resize the state/store gets updated. The default state is "small".

const defaultState = {
    isMobile: true,
    isTablet: false,
    isDesktop: false,
    sizes: {
        small: true,
        medium: false,
        large: false,
        huge: false,
    },
};

In the component which needs to be rendered "adaptive" in two different versions based on the screen size, I simply do a:

if (small) return variation1

if (medium) return variation2

All working.

Now I am facing two problems:

  1. my app is isomorphic, that means the markup renders also server-side. The server doesn't know anything about the user's browser and media queries. So because my default state is "small", the server will always render "variation1". The node-server is the entry point for the site. It looks like the rendering needs to be "delayed" (middleware?) and the server needs to get some information back from the client about the browser width before the React app gets "delivered". Any idea how this problem can be solved?

  2. because the rendering is based on the state, after load "variation 1" can be always seen first for a few milliseconds (flicker), even when the browser size is "desktop". This is because the JS detection takes a few milliseconds before the state gets updated with the current screen width. I think this plays together with above problem and the default state.

I couldn't find any solution for 1, but I guess there must be something isomorphic AND responsive/adaptive.

like image 896
Krad Avatar asked Jun 07 '16 12:06

Krad


1 Answers

A very difficult problem to solve in my opinion with a lot of "it depends" based solutions. :)

I am the author of react-sizeme and react-component-queries, two libraries to help with responsive components, and have experienced similar problems as to which you describe in your question. In my experience I've found that coming up with a solution to one of your problems often affects the other. I'll detail what I mean here by describing my experience below...

I had tried to solve your "problem 2" first:

The flickering of rendering due to the default state was something I experienced during my initial creation of the react-sizeme library. react-sizeme is a higher order component which gets the size available to your component and then passes it into your component. Based on the size you can of course choose to render different components as you have done in your example, so update flickering can occur unless you happen to hit a default state sweet spot. I "conquered" this by changing react-sizeme to initially render an empty placeholder in order to get the available width/height and then only render your Component, giving it the "proper" width/height. This has worked very effectively for me. No longer do I see ComponentBob being rendered, only to be unmounted and have ComponentFoo immediately render in it's place.

Then "problem 1" came along...

react-sizeme started to gain popularity and eventually I had a consumer of the library who wished to use it in a server side rendering context. But because of the fix I put in place for problem 1 the server side rendering would produce a lot of blank content (i.e. the placeholders I was talking about). After the payload was delivered to the browser the placeholder logic would kick in and eventually the size data would be sent to the component and it would be rendered. This is not ideal as you essentially nullify any benefit of doing SSR in the first place. I worked with this user and we decided that the best way forward would be to allow for react-sizeme to be configured to run in "SSR Mode". Basically this entailed dropping the placeholder rendering and allowing for a default Component to be rendered so that you don't get blank pages on the initial server response, but then you can easily suffer the component flickering problem again!

Aaaaaaaaaah! The see saw of affect here! :(

So basically solving one problem directly affects the other.


I've continued to give this some thought and I believe that probably the best way to do this is to try and get the users browser width/height on first request. This would essentially mean rendering a simple utility that fishes out this information and then posts it back to the server with the intention of rendering the user's initial request. You could then use the width/height and pass it through your entire component tree (doing math along the way) to keep on determining what the available height/width is for each component. Super tricky stuff here, but could possibly work.

The other peril of course is that google simply indexes a blank page for the initial request (i.e. the blank render of the util that fishes out the initial width/height). You would have to try and look into using some clever HTTP Response codes such as redirects etc in order to ensure that google follows the trail to the proper rendered output.


Sorry, this may not be the answer you were looking for, but hopefully my experience helps in some manner or provides some sort of inspiration. If you do come up with some interesting experiments please do keep me posted. I would be happy to work with you on this.

like image 80
ctrlplusb Avatar answered Oct 20 '22 06:10

ctrlplusb