gajus / redux-immutable

redux-immutable is used to create an equivalent function of Redux combineReducers that works with Immutable.js state.
Other
1.88k stars 82 forks source link

React-router <Link> does not work #29

Closed andynoelker closed 8 years ago

andynoelker commented 8 years ago

I am using this package with react-redux-router. While manually changing the URL in my browser does follow my routes and take me to the correct page, if I try to use the <Link> component provided by react-router (or even use the push action creator from react-redux-router) nothing happens. A link gets created and I can inspect and see it has the correct href, but clicking on it does nothing.

I believe this is probably an issue with the history not syncing correctly with the store, but I feel like I am following the documentation correctly. Has anyone else been successful in using that component with this package? Not sure if I'm doing something wrong or if this is a real bug.

react v0.14.8 react-router v2.3.0 react-router-redux v4.0.2 redux-immutable v3.0.6

I am adding the LOCATION_CHANGE reducer. Note that while I call it routing here, I have also called it route and routes to no avail.

reducers.js

import Immutable from 'immutable';
import { LOCATION_CHANGE } from 'react-router-redux';

const initialState = Immutable.fromJS({
    location: {}
});

export function routing(state = initialState, action) {
    if (action.type === LOCATION_CHANGE) {
        return state.merge({
            location: action.payload
        })
    }

    return state
}

I have a custom store configuration file where I add combineReducers from this package.

configureStore.js

import { createStore, applyMiddleware } from 'redux'
import promiseMiddleware from './middleware/promise-middleware'
import { routing } from './reducers'
import { combineReducers } from 'redux-immutable'

export default function(data) {
  const reducer = combineReducers({
    routing: routing
  })

  const store = createStore(
    reducer,
    applyMiddleware(promiseMiddleware)
  )

  return store
}

Then I add this store, sync it with the router history, and add it to my app wrapper (that gets loaded into the DOM). This is where I suspect the problems are coming in. Once again, while I call the piece of state routing here, I have also tried route and routes.

app.js

import React from 'react'
import { Component } from 'react'
import { Provider } from 'react-redux'
import { Router, useRouterHistory } from 'react-router'
import { syncHistoryWithStore } from 'react-router-redux'
import { createHistory, useBasename  } from 'history'
import createBrowserHistory from 'history/lib/createBrowserHistory'
import makeRoutes from '../routes'
import createStore from '../configureStore'
import Immutable from 'immutable'

const browserHistory = useRouterHistory(createBrowserHistory)({
  basename: '/baseurl'
})
const store = createStore()
const history = syncHistoryWithStore(browserHistory, store, {
  selectLocationState: (state) => {
    return state.getIn(['routing', 'location']).toJS()
  },
})
const routes = makeRoutes(store)

class App extends Component {
  render() {
    return (
      <Provider store={store}>
        <Router history={history} routes={routes} />
      </Provider>
    )
  }
}

export default App

Any thoughts on why the <Link> component wouldn't be working with a setup like this?

gajus commented 8 years ago

Need to confirm if documentation examples work as expected.

Related to: https://github.com/gajus/redux-immutable/issues/28

andynoelker commented 8 years ago

@gajus Cool! So are you saying that there are other ways of using this package that aren't covered by the documentation?

andynoelker commented 8 years ago

Actually looking over that issue, I am not sure that those changes affect my problem. As in, I notice that they suggest only going one level deep into the route object when syncing history with the store, but if I try that, I still have exactly the same results (manually entering URLs works, but <Link> component does not).

const history = syncHistoryWithStore(browserHistory, store, {
  selectLocationState: (state) => {
    return state.get('routing').toJS()
  },
})

So I am not sure if that gets one step closer to uncovering the issue, but it definitely does not fix anything for me.

jonathancalb commented 8 years ago

I have exactly the same issue. When i go from the URL directly it works, but if y do browserHistory.push(route) or use a Link the route dosn't change.

nathanhoad commented 8 years ago

I had exactly the same issue and ended up reading through https://github.com/reactjs/react-router-redux/blob/v4.0.2/src/sync.js and https://github.com/reactjs/react-router-redux/blob/v4.0.2/src/reducer.js

I then tried changing the location key to locationBeforeTransitions and <Link>s started working like they used to.

reducers/routing.js

const Immutable = require('immutable');
const ReactRouterRedux = require('react-router-redux');

const initial_state = Immutable.fromJS({
    locationBeforeTransitions: null
});

function routing (state = initial_state, action) {
    if (action.type === ReactRouterRedux.LOCATION_CHANGE) {
        return state.merge({
            locationBeforeTransitions: action.payload
        });
    }

    return state;
}

module.exports = routing;

Snippet from store.js

const history = ReactRouterRedux.syncHistoryWithStore(ReactRouter.browserHistory, store, {
    selectLocationState (state) {
        return state.get('routing').toJS();
    }
});
andynoelker commented 8 years ago

I can confirm that the new changes to the documentation (mainly changing the reducer property to locationBeforeTransitions) have fixed the <Link> component and any issues I had directly pushing a path to the router.