title: pushState in React + Redux using connected-react-router
body: <p>I want to push state to the browser and append to the pathname when a subreddit has changed.</p>
  
  <p>In the example below the user chooses an option from <code>['reactjs', 'frontend']</code>. So when the user chooses <code>reactjs</code>, I want to changethe browser url to: <code>&lt;url&gt;/reddit/reactjs</code> or <code>&lt;url&gt;/reddit/frontend</code> based on the selection.</p>
  
  <p>So when the user goes back and forward, I want to show data that was already fetched.</p>
  
  <p>How can I make it work with react-redux for the example below? Normally, I was using <code>history.pushState(...)</code>.</p>
  
  <p>Note: I am using <code>connected-react-router</code></p>
  
  <p><strong>index.js:</strong></p>
  
  <pre><code>import 'babel-polyfill'
  
  import React from 'react'
  import { render } from 'react-dom'
  import Root from './containers/Root'
  
  render(&lt;Root /&gt;, document.getElementById('root'))
  </code></pre>
  
  <p><strong>action.js:</strong></p>
  
  <pre><code>import fetch from 'cross-fetch'
  
  export const REQUEST_POSTS = 'REQUEST_POSTS'
  export const RECEIVE_POSTS = 'RECEIVE_POSTS'
  export const SELECT_SUBREDDIT = 'SELECT_SUBREDDIT'
  export const INVALIDATE_SUBREDDIT = 'INVALIDATE_SUBREDDIT'
  
  export function selectSubreddit(subreddit) {
    return {
      type: SELECT_SUBREDDIT,
      subreddit
    }
  }
  
  export function invalidateSubreddit(subreddit) {
    return {
      type: INVALIDATE_SUBREDDIT,
      subreddit
    }
  }
  
  function requestPosts(subreddit) {
    return {
      type: REQUEST_POSTS,
      subreddit
    }
  }
  
  function receivePosts(subreddit, json) {
    return {
      type: RECEIVE_POSTS,
      subreddit,
      posts: json.data.children.map(child =&gt; child.data),
      receivedAt: Date.now()
    }
  }
  
  function fetchPosts(subreddit) {
    return dispatch =&gt; {
      dispatch(requestPosts(subreddit))
      return fetch(`https://www.reddit.com/r/${subreddit}.json`)
        .then(response =&gt; response.json())
        .then(json =&gt; dispatch(receivePosts(subreddit, json)))
    }
  }
  
  function shouldFetchPosts(state, subreddit) {
    const posts = state.postsBySubreddit[subreddit]
    if (!posts) {
      return true
    } else if (posts.isFetching) {
      return false
    } else {
      return posts.didInvalidate
    }
  }
  
  export function fetchPostsIfNeeded(subreddit) {
    return (dispatch, getState) =&gt; {
      if (shouldFetchPosts(getState(), subreddit)) {
        return dispatch(fetchPosts(subreddit))
      }
    }
  }
  </code></pre>
  
  <p><strong>reducers.js:</strong></p>
  
  <pre><code>import { combineReducers } from 'redux'
  import {
    SELECT_SUBREDDIT,
    INVALIDATE_SUBREDDIT,
    REQUEST_POSTS,
    RECEIVE_POSTS
  } from './actions'
  
  function selectedSubreddit(state = 'reactjs', action) {
    switch (action.type) {
      case SELECT_SUBREDDIT:
        return action.subreddit
      default:
        return state
    }
  }
  
  function posts(
    state = {
      isFetching: false,
      didInvalidate: false,
      items: []
    },
    action
  ) {
    switch (action.type) {
      case INVALIDATE_SUBREDDIT:
        return Object.assign({}, state, {
          didInvalidate: true
        })
      case REQUEST_POSTS:
        return Object.assign({}, state, {
          isFetching: true,
          didInvalidate: false
        })
      case RECEIVE_POSTS:
        return Object.assign({}, state, {
          isFetching: false,
          didInvalidate: false,
          items: action.posts,
          lastUpdated: action.receivedAt
        })
      default:
        return state
    }
  }
  
  function postsBySubreddit(state = {}, action) {
    switch (action.type) {
      case INVALIDATE_SUBREDDIT:
      case RECEIVE_POSTS:
      case REQUEST_POSTS:
        return Object.assign({}, state, {
          [action.subreddit]: posts(state[action.subreddit], action)
        })
      default:
        return state
    }
  }
  
  const rootReducer = combineReducers({
    postsBySubreddit,
    selectedSubreddit
  })
  
  export default rootReducer
  </code></pre>
  
  <p><strong>configureStore.js</strong></p>
  
  <pre><code>import { createStore, compose, applyMiddleware } from 'redux'
  import { createBrowserHistory } from 'history'
  import { routerMiddleware } from 'connected-react-router'
  import thunkMiddleware from 'redux-thunk'
  import logger from 'redux-logger'
  import rootReducer from '../reducers'
  
  
  // const loggerMiddleware = createLogger()
  
  
  export const history = createBrowserHistory()
  
  export default function configureStore(preloadedState?: any) {
    const store = createStore(
      rootReducer(history), // root reducer with router state
      preloadedState,
      compose(
        applyMiddleware(
          thunkMiddleware,
          logger,
          routerMiddleware(history), // for dispatching history actions
          // ... other middlewares ...
        ),
      ),
    )
  
    return store
  }
  
  </code></pre>
  
  <p><strong>Root.js</strong></p>
  
  <pre><code>import React, { Component } from 'react'
  import { Provider } from 'react-redux'
  import configureStore from '../configureStore'
  import AsyncApp from './AsyncApp'
  
  const store = configureStore()
  
  export default class Root extends Component {
    render() {
      return (
        &lt;Provider store={store}&gt;
          &lt;AsyncApp /&gt;
        &lt;/Provider&gt;
      )
    }
  }
  </code></pre>
  
  <p><strong>AsnycApp.js</strong></p>
  
  <pre><code>import React, { Component } from 'react'
  import PropTypes from 'prop-types'
  import { connect } from 'react-redux'
  import {
    selectSubreddit,
    fetchPostsIfNeeded,
    invalidateSubreddit
  } from '../actions'
  import Picker from '../components/Picker'
  import Posts from '../components/Posts'
  
  class AsyncApp extends Component {
    constructor(props) {
      super(props)
      this.handleChange = this.handleChange.bind(this)
      this.handleRefreshClick = this.handleRefreshClick.bind(this)
    }
  
    componentDidMount() {
      const { dispatch, selectedSubreddit } = this.props
      dispatch(fetchPostsIfNeeded(selectedSubreddit))
    }
  
    componentDidUpdate(prevProps) {
      if (this.props.selectedSubreddit !== prevProps.selectedSubreddit) {
        const { dispatch, selectedSubreddit } = this.props
        dispatch(fetchPostsIfNeeded(selectedSubreddit))
      }
    }
  
    handleChange(nextSubreddit) {
      this.props.dispatch(selectSubreddit(nextSubreddit))
      this.props.dispatch(fetchPostsIfNeeded(nextSubreddit))
    }
  
    handleRefreshClick(e) {
      e.preventDefault()
  
      const { dispatch, selectedSubreddit } = this.props
      dispatch(invalidateSubreddit(selectedSubreddit))
      dispatch(fetchPostsIfNeeded(selectedSubreddit))
    }
  
    render() {
      const { selectedSubreddit, posts, isFetching, lastUpdated } = this.props
      return (
        &lt;div&gt;
          &lt;Picker
            value={selectedSubreddit}
            onChange={this.handleChange}
            options={['reactjs', 'frontend']}
          /&gt;
          &lt;p&gt;
            {lastUpdated &amp;&amp; (
              &lt;span&gt;
                Last updated at {new Date(lastUpdated).toLocaleTimeString()}.{' '}
              &lt;/span&gt;
            )}
            {!isFetching &amp;&amp; (
              &lt;button onClick={this.handleRefreshClick}&gt;Refresh&lt;/button&gt;
            )}
          &lt;/p&gt;
          {isFetching &amp;&amp; posts.length === 0 &amp;&amp; &lt;h2&gt;Loading...&lt;/h2&gt;}
          {!isFetching &amp;&amp; posts.length === 0 &amp;&amp; &lt;h2&gt;Empty.&lt;/h2&gt;}
          {posts.length &gt; 0 &amp;&amp; (
            &lt;div style={{ opacity: isFetching ? 0.5 : 1 }}&gt;
              &lt;Posts posts={posts} /&gt;
            &lt;/div&gt;
          )}
        &lt;/div&gt;
      )
    }
  }
  
  AsyncApp.propTypes = {
    selectedSubreddit: PropTypes.string.isRequired,
    posts: PropTypes.array.isRequired,
    isFetching: PropTypes.bool.isRequired,
    lastUpdated: PropTypes.number,
    dispatch: PropTypes.func.isRequired
  }
  
  function mapStateToProps(state) {
    const { selectedSubreddit, postsBySubreddit } = state
    const { isFetching, lastUpdated, items: posts } = postsBySubreddit[
      selectedSubreddit
    ] || {
      isFetching: true,
      items: []
    }
  
    return {
      selectedSubreddit,
      posts,
      isFetching,
      lastUpdated
    }
  }
  
  export default connect(mapStateToProps)(AsyncApp)
  </code></pre>
  
  <p><strong>Picker.js</strong></p>
  
  <pre><code>import React, { Component } from 'react'
  import PropTypes from 'prop-types'
  
  export default class Picker extends Component {
    render() {
      const { value, onChange, options } = this.props
  
      return (
        &lt;span&gt;
          &lt;h1&gt;{value}&lt;/h1&gt;
          &lt;select onChange={e =&gt; onChange(e.target.value)} value={value}&gt;
            {options.map(option =&gt; (
              &lt;option value={option} key={option}&gt;
                {option}
              &lt;/option&gt;
            ))}
          &lt;/select&gt;
        &lt;/span&gt;
      )
    }
  }
  
  Picker.propTypes = {
    options: PropTypes.arrayOf(PropTypes.string.isRequired).isRequired,
    value: PropTypes.string.isRequired,
    onChange: PropTypes.func.isRequired
  }
  </code></pre>
  
  <p><strong>Posts.js</strong>:</p>
  
  <pre><code>import React, { Component } from 'react'
  import PropTypes from 'prop-types'
  
  export default class Posts extends Component {
    render() {
      return (
        &lt;ul&gt;
          {this.props.posts.map((post, i) =&gt; (
            &lt;li key={i}&gt;{post.title}&lt;/li&gt;
          ))}
        &lt;/ul&gt;
      )
    }
  }
  
  Posts.propTypes = {
    posts: PropTypes.array.isRequired
  }
  </code></pre>
  
  <hr>
  
  <p><strong>Update</strong>:</p>
  
  <pre><code>import { push } from 'connected-react-router';
  ...
  handleChange(nextSubreddit) {
    this.props.dispatch(push('/reddit/' + nextSubreddit))
  }
  </code></pre>
  
  <p>I placed this in the <code>handleChange()</code> method. When Picker changes, I push the state to the browser. However, when I go back and forward, the data does not change according to this url. I see the same data in every state.</p>