A lot of anti-pattern code, poor application structured, and mixing of packages is holding your application back.
I rewrote it entirely, here's what I've done:
- Reconfigured your application folder's structure to be standard.
- Don't mix
Router
(BrowserRouter
) with ConnectedRouter
.
- Don't place all of your
components
within the App.js
file.
- Since the
Header
is always mounted, you don't need redux
, instead you can just use withRouter
(it exposes route props to the component).
- Your
rootReducer
is missing a reducer
, so I added a dummyReducer
that just returns state
.
- Stick to
Link
or this.props.history
when navigating. For this example, there's no need to use both. Also, you don't need to use ConnectedRouter
's push
function, because the history
is passed as a prop when using withRouter
.
Side note: If you want the Header
to be a "router" where all route changes pass through here, then you'll need to create an action
and a reducer
that passes a string
and stores it to the redux's store
. The Header
is then connect
ed to the redux store and updates the route
when this string
has changed.
Working example: https://codesandbox.io/s/526p7kjqq4
components/Header.js
import React, { PureComponent, Fragment } from "react";
import { withRouter } from "react-router-dom";
class Header extends PureComponent {
goTo = route => {
this.props.history.push(route);
};
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default withRouter(Header);
routes/index.js
import React from "react";
import { Switch, Route } from "react-router-dom";
import Announcements from "../components/annoucements";
import Shopping from "../components/shopping";
export default () => (
<div style={{ padding: "150px" }}>
<Switch>
<Route exact path="/" component={Announcements} />
<Route path="/shopping" component={Shopping} />
</Switch>
</div>
);
components/App.js
import React, { Fragment } from "react";
import Routes from "../routes";
import Header from "./Header";
export default () => (
<Fragment>
<Header />
<Routes />
</Fragment>
);
Here is what you're trying to accomplish: https://codesandbox.io/s/8nmp95y8r2
However, I DO NOT recommend this as it's a bit unnecessary, when history
is either already passed as a prop from the Route
or can be exposed when using withRouter
. According to the Redux docs, it's not recommended either. And instead to either use Link
or pass the history
prop to the redux action
creator instead of programmatic navigation through redux state.
containers/Header.js
import React, { PureComponent, Fragment } from "react";
import { connect } from "react-redux";
import { push } from "connected-react-router";
class Header extends PureComponent {
goTo = route => this.props.push(route); // this is equivalent to this.props.dispatch(push(route)) -- I'm just appending dispatch to the push function in the connect function below
render = () => (
<Fragment>
<ul>
<li>
<button onClick={() => this.goTo("/")}> Announcements </button>
</li>
<li>
<button onClick={() => this.goTo("/shopping")}> Shopping </button>
</li>
</ul>
<div>
<button onClick={() => this.goTo("/shopping")}>
Click here to go shopping ! (if you can...)
</button>
</div>
</Fragment>
);
}
export default connect(
null,
{ push }
)(Header);