I'm learning redux and created a Navbar component that will handle user actions. It uses NavLink to add the 'active' class to my links so I can style the active link. When I navigate my site however the active class doesn't move until I send an action to react (login/logout is all I"ve done so far). If I take out the connect stuff and use the class component directly the NavLinks work fine. Adding a log to componentDidUpdate shows that it isn't being called when connect is used.
Am I missing something? I see there's react-router-redux but I'm building a site for fun to learn the whole deal and I'm just starting to get redux, I don't want to bring in the concept of middleware and I definitely don't want to use combineReducers since at this point it would just mean having the router property and shoving everything else under something like 'main' in the state.
I'm thinking of using context and putting the whole redux state on it...
update: - I added react-router-redux and I can only get it to update on a route change if I add the new 'router' to my mapped props.
I have a NavbarC component class with a render method like this:
render() {
let { props } = this;
return <header className="navbar">
<nav>
<h1><NavLink to="/" exact>Home</NavLink></h1>
<NavLink to="/friends">Friends</NavLink>
</nav>
<div></div>
<div className="center-flex-row">
{ props.busy ? <section className="nav-right"><p>working<span className="spacer"></span><span className="fa fa-spin fa-spinner"></span></p></section> :
props.user ?
<Logout onSignOutClick={props.onSignOutClick} user={props.user} /> :
<Login onSignInClick={props.onSignInClick}/>
}
</div>
</header>
}
And I use connect():
const mapStateToProps = (state) => {
return { user: state.auth.user, busy: state.auth.busy
}
}
const mapDispatchToProps = (dispatch) => {
return {
onSignInClick: () => {
dispatch(userLogin());
},
onSignOutClick: () => {
dispatch(userLogout());
}
}
}
const Logout = ({ user, onSignOutClick }) => {
return <section className="nav-right">
<button className="btn" onClick={ onSignOutClick }>Sign Out</button>
</section>
}
const Login = ({ onSignInClick }) => {
return <section className="nav-right">
<button className="btn" onClick={ onSignInClick }>Sign In</button>
</section>
}
export const Navbar = connect(mapStateToProps, mapDispatchToProps)(NavbarC)
I got it to work in three ways. I think connect is ignoring the props by default and replacing them, the example I based my code on didn't use 'ownProps'. So If I put the component in a route:
<Route path="/" component={Navbar}/>
And merge the properties in my mapStateToProps:
const mapStateToProps = (state, ownProps) => {
console.log('mapping state:', state);
console.log(' ownProps:', ownProps);
return {
user: state.main.auth.user,
busy: state.main.auth.busy,
...ownProps
}
}
That puts in the props from react-router (history, location, match) and causes the component to update because those values change when the route changes.
Another way is to bite the bullet and use react-router-redux, which puts a 'router' property on my state, which I then have to add to my mapStateToProps. Even though I don't care about the value, connect will detect that the props are different and re-render the component:
let store = createStore(
combineReducers({
main,
router: routerReducer
}),
applyMiddleware(middleware),
}
// in Navbar.js
const mapStateToProps = (state, ownProps) => {
return {
user: state.main.auth.user,
busy: state.main.auth.busy,
router: state.router
}
}
I can also specify pure=false in the options to connect() and that should re-render my component every time (at least it updates the right NavLink when I change routes for sure):
export const Navbar = connect(mapStateToProps, mapDispatchToProps,
undefined, { pure: false })(NavbarC)
The best and easiest way to fix this issue is by doing
export default connect(mapStateToProps, mapDispatchToProps, undefined, { pure: false })(NavbarC)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With