Search code examples
javascriptreactjsreduxreact-routerreact-router-redux

React-Router <Link> component doesn't get activeStyle applied, after connecting redux-store


I recently started learning React / Redux and right now I'm trying to build a small single page application.

Lately I ran into an issue that I did't understand and couldn't fix.

Let me show you the code:

index.js

import React from 'react'
import ReactDOM from 'react-dom'
import { createStore, combineReducers } from 'redux'
import { Provider } from 'react-redux'
import { Router, Route, IndexRoute, hashHistory } from 'react-router'
import { syncHistoryWithStore, routerReducer } from 'react-router-redux'

import App from './components/App'
import Home from './components/Home'
import About from './components/About'

const reducer = (state = {}, action) => {
  return state
}

const store = createStore(
  combineReducers({
    reducer,
    routing: routerReducer
  })
)

const history = syncHistoryWithStore(hashHistory, store)

ReactDOM.render((
  <Provider store={store}>
    <Router history={history}>
      <Route path='/' component={App}>
        <IndexRoute component={Home}/>

        <Route path='about' component={About}/>
      </Route>
    </Router>
  </Provider>
), document.getElementById('root'))

Basically this is the example from the to react-router-redux GitHub page (scroll down to the section Tutorial) - the only difference is, that I am using a hashHistory.

App.js

import React, { Component, PropTypes } from 'react'

import Navigation from './Navigation'

export default class App extends Component {
  render() {
    return (
      <div>
        <Navigation/>

        {this.props.children}
      </div>
    )
  }
}

App.propTypes = {
  children: PropTypes.node
}

Navigation.js

import React, { Component } from 'react'
import { Link, IndexLink } from 'react-router'

export default class Navigation extends Component {
  render() {
    return (
      <ul role='nav'>
        <li><IndexLink to='/' activeStyle={{ color: 'red' }}>Home</IndexLink></li>
        <li><Link to='/about' activeStyle={{ color: 'red' }}>About</Link></li>
      </ul>
    )
  }
}

The Home and About components are just rendering a <h1> Headline to visualize the state of the SPA.

So far, everything works fine as you would expect it! When the user clicks the About link, the URL changes accordingly and the link turns red (because of the activeStyle property).

But here comes my problem (thanks for still reading this btw):

I want to wrap my <Navigation> component via react-redux connect(). So this is my new version

Navigation.js

import React, { Component } from 'react'
import { connect } from 'react-redux'
import { Link, IndexLink } from 'react-router'

class Navigation extends Component {
  render() {
    return (
      <ul role='nav'>
        <li><IndexLink to='/' activeStyle={{ color: 'red' }}>Home</IndexLink></li>
        <li><Link to='/about' activeStyle={{ color: 'red' }}>About</Link></li>
      </ul>
    )
  }
}

const NavigationContainer = connect()(Navigation)

export default NavigationContainer

But now the activeStyle feature seems to be broken... When I navigate through my app, the currently active link does not change its color anymore.

I really tried to solve this issue for hours :( Any help is very appreciated!


Solution

  • react-redux's connect() preventing Navigation , and subsequently links, from rerendering on a location state change.

    If you add console.log("Navigation render") in Navigation's render , you will see that after you add connect() func, component doesnt re-render on a location state change.


    There are ways to avoid it

    1.way: in order to re-render Navigation comp on location change, you can add prop , something like that location={this.props.location}

    export default class App extends Component {
      render() {
        return (
          <div>
            <Navigation location={this.props.location}/>
    
            {this.props.children}
          </div>
        )
      }
    }
    

    2.way: add {pure:false} to connect(). DOCS LINK

    export default connect(mapStateToProps, actions, null, { pure: false })(Navigation);
    

    3.way: This is fixed in React Router 3.0.0-alpha.1 and newer.