Search code examples
javascriptreactjsreact-router-v4code-splittingreact-loadable

React Router 4 + Code splitting | Component remounts


I have the next code:

import React from 'react'
import Loadable from 'react-loadable'
import { Route } from 'react-router-dom'

class App extends React.Component {
    state = {
        kappa: false
    }

    componentDidMount() {
        setTimeout(() => {
            setState({ kappa: true })
        }, 1000)
    }

    render() {
        return (
            <div className="app">
                <Route exact path="/:locale/" component={Loadable({
                    loader: () => import('../views/IndexPage/index.jsx'),
                    loading: () => "loading"
                })} />
                <Route exact path="/:locale/registration" component={Loadable({
                    loader: () => import('../views/Registration/index.jsx'),
                    loading: () => "loading"
                })} />

                {this.state.kappa && <p>Hey, Kappa-Kappa, hey, Kappa-Kappa, hey!</p>}
            </div>
        )
    }
}

When state updates (kappa becomes true and p appears), component on active route (no matter what is it - IndexPage or Registration) remounts. If I import component manually in App and pass it to the Route without code-splitting, components on routes doesn't remount (that's so obvious).

I also tried webpack's dynamic import, like this:

<Route path="/some-path" component={props => <AsyncView {...props} component="ComponentFolderName" />

where import(`/path/to/${this.props.component}/index.jsx`) runs in componentDidMount and upfills AsyncView's state, and it behaves simillar to Loadable situation.

I suppose, the problem is that component for Route is an anonymous function

The question is: how to avoid remount of route components?


Solution

  • Well, this behaviour is normal and documented at React Router 4 docs:

    When you use component (instead of render or children, below) the router uses React.createElement to create a new React element from the given component. That means if you provide an inline function to the component prop, you would create a new component every render. This results in the existing component unmounting and the new component mounting instead of just updating the existing component. When using an inline function for inline rendering, use the render or the children prop (below).

    render works fine both with React Loader and webpack's code splitting.