I know that this question has been asked before, but I keep having issues with this.
The issue I have is that when I use a Page Layout -like component to wrap my routes, this page layout is re-rendered when changing path.
In react-router v3 I did something like this:
<Router history={this.props.history}>
<Route path="/">
<IndexRedirect to="/Dossiers" />
<Route path="/Dossiers" component={MainLayout}>
<IndexRoute component={DossiersPage} />
<Route path="/Dossiers/:dossierId/:title" component={DossierDetailsPage} />
</Route>
</Route>
</Router>
When moving paths, this would NOT re-render the MainLayout
component (which is easily checked by putting something in state inside MainLayout
).
Now, in react-router v4 I tried a couple of approaches already:
Switch
with the MainLayout
componentRouteWithMainLayout
component which wraps Route
(as described here: https://simonsmith.io/reusing-layouts-in-react-router-4/ ) However, all solutions I've tried seem to re-render the MainLayout
component, basically causing state
to reset to its initial value(s).
tldr; How do I create a wrapping component in react-router v4
which doesn't re-render when changing paths
I put together a codesandbox example of how I'm using a "page layout" type of component. It uses React Router v4.1.2.
https://codesandbox.io/s/Vmpy1RzE1
As you described in your question, and as was described in Matt's answer, the MainLayout
component wraps the routes.
<BrowserRouter>
<MainLayout>
<Switch>
<Route path="/" exact component={Home} />
<Route path="/about" exact component={About} />
</Switch>
</MainLayout>
</BrowserRouter>
It is true that the MainLayout
component re-renders when I navigate the app, in the sense that render
is called by React. But, the MainLayout
component is never unmounted, so the state never re-initializes.
I've placed some console.log
s around my example to show this. My MainLayout
looks like this:
export default class MainLayout extends React.Component {
state = {
layoutCreatedOn: Date(),
};
componentDidMount() {
//This will not fire when we navigate the app.
console.log('layout did mount.');
}
componentWillUnmount() {
//This won't fire,
// because our "shared page layout" doesn't unmount.
console.log('layout will unmount');
}
render() {
//This does fire every time we navigate the app.
// But it does not re-initialize the state.
console.log('layout was rendered');
return (
<div styles={styles}>
<h5>
Layout created: {this.state.layoutCreatedOn}
</h5>
<Sidebar />
{this.props.children}
</div>
);
}
}
As you click around the app, you'll see a few things.
componentDidMount
fires only once. componentWillUnmount
never fires. render
fires every time you navigate.layoutCreatedOn
property shows the same time as I navigate the app. The state is initialized when the page loads, and never re-initialized.You no longer need IndexRedirect
, instead just wrap all of your routes in your MainLayout
component, such as:
<Router history={this.props.history}>
<Switch>
<MainLayout>
<Route path="/" component={DossiersPage}/>
<Route path="/Dossiers/:dossierId/:title" component={DossierDetailsPage} />
</MainLayout>
</Switch>
</Router>
Here is the correct solution for React Router v4 as stated here
So basically you need to use the render method to render the layout and wrap your component like this:
<Router>
<Switch>
<Route path={ROUTES.LOGIN} render={props =>
<LoginLayout {...props}>
<Login {...props} />
</LoginLayout>
} />
<Route path={ROUTES.REGISTER} render={props =>
<LoginLayout {...props}>
<Register {...props} />
</LoginLayout>
} />
<Route path="*" component={NotFound} />
</Switch>
</Router>
This will not cause re-rendering of the layout when you are changing the routes.
When you have many different components with many different layouts you can go ahead and define them in a route config array like the example from the issue I linked :
const routes = [
{ path: '/',
exact: true,
component: Home
},
{ path: '/about',
component: About,
},
{ path: '/cart',
component: Three,
}
]
<Router>
<Switch>
{routes.map({ path, exact, component: Comp } => (
<Route path={path} exact={exact} render={(props) => (
<LayoutWithSidebarAndHeader {...props}>
<Comp {...props}/>
</LayoutWithSidebarAndHeader>
)}/>
))}
<Route component={Error404}/>
</Switch>
</Router>
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.