Search code examples
javascriptreactjsserver-side-renderingreact-domreact-dom-server

How does ReactDOMServer behave when code needs the device width?


I have the following hook that I use to build responsive components throughout my App.

I looking into implementing Server Side Rendering.

useWindowWidth.js

import {useEffect, useRef, useState} from 'react';

function useWindowWidth() {
  const hasResized = useRef(false);
  const [windowSize,setWindowSize] = useState(window.innerWidth);

  useEffect(() => {

    function handleResize() {
      if (hasResized.current === true) {
        return;
      }
      hasResized.current = true;
      throtled_forceUpdate();
    }

    function throtled_forceUpdate() {
      setTimeout(()=>{
        // console.log('I will be updated!');
        // console.log(window.innerWidth);
        setWindowSize(window.innerWidth);
        hasResized.current = false;
      },250);
    }

    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);

  return windowSize;

}

export default useWindowWidth;

App.js

import React from 'react';
import useWindowWidth from './hooks/useWindowWidth';

function App(props) {

  const windowWidth = useWindowWidth();

  return(
    windowWidth > 1200 ?
      <DesktopLayout/>
    : <MobileLayout/>
  );
}

ReactDOMServer

ReactDOMServer.renderToString(App);

What is the ReactDOMServer behavior when it encounters something like this? What is the workaround for this kind of situation?


Solution

  • There is no way to get the window width on the server side. A workaround would be to use the useragent from the incoming request to determine which component should be rendered on the server.

    const deviceType = useDeviceType();
    return deviceType === 'mobile' ? <MobileLayout /> : <DesktopLayout />;
    

    Or as the docs mention:

    If you intentionally need to render something different on the server and the client, you can do a two-pass rendering. Components that render something different on the client can read a state variable like this.state.isClient, which you can set to true in componentDidMount(). This way the initial render pass will render the same content as the server, avoiding mismatches, but an additional pass will happen synchronously right after hydration. Note that this approach will make your components slower because they have to render twice, so use it with caution.