How to properly hydrate react app with code splitting on client

1.1k views Asked by At

I am building a react app and I did code splitting on the client for the bundle. My app properly render on the server as I am using SSR, I have no code Splitting on the server but on the client I do. I am getting the following warning on client with hydrate.

Warning: Did not expect server HTML to contain a <div> in <div>.

I am using @loadable/component for code splitting on the client

I have my App.js file on the client side as:-

import React from "react" 
import loadable from '@loadable/component'
import { Route, Switch } from 'react-router-dom'

const AsyncHome = loadable(() =>
  import('./components/Home/Home')  
)
const AsyncPost = loadable(() =>
  import('./components/Post/Post')
)

function App(){
   return(
       <div>
         <Switch>
             <Route path="/" component={AsyncHome}/>
             <Route path="post" component={AsyncPost}/>
           </Switch>
       </div>
    )
}

I have my index.js file on the client as:

import React from 'react'
import { hydrate } from 'react-dom'
import { BrowserRouter} from 'react-router-dom'
import App from './App'

hydrate(
    <BrowserRouter><App/></BrowserRouter>,
    document.getElementById('app')
)

How to get fix that warning with using code-splitting only on the client. I have also tried React.lazy and React.Suspense but i am getting the same error.

1

There are 1 answers

5
Ravi Chaudhary On

You would need to ensure container that you are trying to hydrate on client side i same as the one rendered on server side. Please have a look at the react documentation

Having said that, code splitting is done to achieve smaller JS bundles which means we plan well in advance how to we plan to split our code based on our use cases. We don't do that runtime on client side. Still, I might be missing something so it would help if you could share the reason for trying out to do so. On the server side though, you have the option to do it as follows:

const AsyncHome = loadable(() =>
  import(
    /* webpackChunkName: "home" */
    /* webpackPrefetch: true */
    './components/Home'
  ),
);
const AsyncPost = loadable(() =>
  import(
    /* webpackChunkName: "post" */
    /* webpackPrefetch: true */
    './components/Post'
  ),
);

Another better way to achieve the same thing is by using ChunkExtractor plugin from @loadable/server if you don't want to explicitly keep track of your code split chunks in the app Also, to avoid rendering on server side, you need to ensure component is initialized when mounted which would happen only on client side only

let HomeComponent = () => <div/>
let PostComponent = () => <div/>

function App(){
   useEffect(() => {
    HomeComponent = loadable(() =>
      import(
        /* webpackChunkName: "home" */
        /* webpackPrefetch: true */
        './components/Home'
      ),
    );
    PostComponent = loadable(() =>
      import(
        /* webpackChunkName: "post" */
        /* webpackPrefetch: true */
        './components/Post'
      ),
    );
  }, []);
}