I have been reading about this issue and I want to clarify what is going on. I am using the last version of the react router and I want to do the routing through redux. I follow the steps that are listed in the documentation of the redux router module, but I receive this error when I make the implementation: (I know that the issue is in the server render)
Invariant Violation: Browser history needs a DOM
Here is my code (the important parts):
server.js
import { Provider } from 'react-redux';
import store from './store';
lisaApp.get('*', function (req, res) {
const context = {};
const html = renderToString(
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<StaticRouter location={req.url} context={context}>
<Routes />
</StaticRouter>
</MuiThemeProvider>
</Provider>,
);
res.setHeader('Content-Type', 'text/html');
if (context.url) {
res.writeHead(301, {
Location: context.url,
});
res.end();
}
res.write(
renderToStaticMarkup(<Layout title={req.title} content={html} />),
);
res.end();
}
client.js
import { Provider } from 'react-redux';
import createHistory from 'history/createBrowserHistory';
import { BrowserRouter } from 'react-router-dom';
import store from './store';
render((
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<BrowserRouter history={createHistory()}>
<Routes />
</BrowserRouter>
</MuiThemeProvider>
</Provider>),
document.getElementById('app'));
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux'
import createHistory from 'history/createBrowserHistory'
import { ConnectedRouter, routerReducer, routerMiddleware, push } from 'react-router-redux'
import thunk from 'redux-thunk';
import reducer from './reducer';
const history = createHistory()
const middlewareHistory = routerMiddleware(history)
const store = createStore(
combineReducers({
reducer,
router: routerReducer
}),
applyMiddleware(
middlewareHistory,
thunk
)
);
export default store;
component.js (dispatch)
const mapDispatchToProps = dispatch => {
return {
onNavigateTo(dest) {
dispatch(push(dest));
}
};
};
Obviously the dispatch, from my component never is called. Anyone can me clarify if maybe this feature is not implemented yet in the react router redux module? In advance Thanks.
4.0.0
webpack.config.js (server)
{
new webpack.DefinePlugin({
'process.env.BROWSER': false,
}),
}
webpack.config.js (client)
{
new webpack.DefinePlugin({
'process.env.BROWSER': true,
}),
}
history.js
import createBrowserHistory from 'history/createBrowserHistory';
import createMemoryHistory from 'history/createMemoryHistory';
export default process.env.BROWSER ? createBrowserHistory() : createMemoryHistory();
client.js
import history from './history';
render((
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<BrowserRouter history={history}>
<Routes />
</BrowserRouter>
</MuiThemeProvider>
</Provider>),
document.getElementById('app')
);
store.js
import history from './history';
const middlewareHistory = routerMiddleware(history)
Hi @Wind4, thanks for your answer. I tried your solution and it works. When I compiled my app, it doesn't show me the error anymore. But the 'dispatch' of the 'push' action of the router doesn't fire any action. It seems that it is called because the logger shows the data of the action, but it never redirects as I expect.
Here is my code
store.js
import { createStore, combineReducers, applyMiddleware } from 'redux';
import { routerReducer, routerMiddleware, push } from 'react-router-redux';
import { createLogger } from 'redux-logger';
import thunk from 'redux-thunk';
import history from './history';
import reducer from './reducer';
const middlewareHistory = routerMiddleware(history);
const store = createStore(
combineReducers({
reducer,
router: routerReducer
}),
applyMiddleware(
createLogger(),
middlewareHistory,
thunk
)
);
export default store;
actions.js
import { push } from 'react-router-redux';
import store from './store';
function goToUrl(dest) {
return (dispatch, getState) => {
store.dispatch(replace(dest));
};
}
export default {
goToUrl
}
Component (where I call the dispatch, I post the important parts)
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';
import actions from '../../actions';
class AppPage extends React.Component {
....
handleDashboardClick(event) {
event.preventDefault();
this.props.actions.goToUrl('/dashboard');
}
....
render() {
return (
<nav>
<Button onClick={this.handleDashboardClick} />
</nav>
);
}
}
function mapDispatchToProps(dispatch) {
return {
actions: bindActionCreators(actions, dispatch)
}
}
export default connect(mapStateToProps, mapDispatchToProps)(AppPage);
In advance, thanks
Hi @maooricio , Change to ConnectedRouter
component
client.js
import { ConnectedRouter } from 'react-router-redux';
import history from './history';
render((
<Provider store={store}>
<MuiThemeProvider muiTheme={getMuiTheme()}>
<ConnectedRouter history={history}>
<Routes />
</ConnectedRouter>
</MuiThemeProvider>
</Provider>),
document.getElementById('app')
);
Well @Wind4, you are absolutely right. I don't know if this is a good example of implementation in order to post on the documentation. I read there that they need a "A working example". What do you think?
But thanks again.
This is a bug tracker, not a support system. For usage questions, please use Stack Overflow or Reactiflux. Thanks!
And more specifically with the previous code, your problem was here:
import createHistory from 'history/createBrowserHistory'
You can't use a browser history on the server side. You need to have a memory history.
I had similar issue to maooricio, regarding push not working. Issue was not with wrong history, it was with order of store enhancers. Specifying applyMiddleware(routerStoreMiddleware)
as first argument in compose helped.
Most helpful comment
webpack.config.js (server)
webpack.config.js (client)
history.js
client.js
store.js