Hello,
I am developing an app to use native webpack HMR and Redux, _without_ react-hot-loader or react-transform. Packages in my project include:
react @ 15.2.1react-dom @ 15.2.1react-redux @ 4.4.5react-router @ 2.6.0react-router-redux @ 4.0.5redux @ 3.5.2webpack @ 2.1.0-beta.19webpack-dev-middleware @ 1.6.1webpack-hot-middleware @ 2.12.1I am using functional components, and key files to the hot reloading are:
routes.js
export default (props) => (
<Router history={props.history}>
<Route path='/' component={App}>
<IndexRoute component={Home} />
<Route path='page' component={Page} />
</Route>
</Router>
);
components/index.js
const render = () => {
ReactDOM.render(
<Provider store={store}>
<Routes history={history} />
</Provider>,
document.getElementById('app')
);
}
if(process.env.NODE_ENV == 'development' && module.hot) {
module.hot.accept(['./components/routes', './reducers'], () => {
store.replaceReducer(require('./reducers').default);
render();
});
}
render();
I was able to hot reload dom elements successfully. However, I faced two minor issues when trying to hot reload reducers.
Issue 1
When the initial state of a reducer is updated, it does not get reflected in the DOM. Also, I get a <Router routes> warning in console.
export default (state = [
{ id: 1, text: 'Todo 1', checked: false },
{ id: 2, text: 'Todo 2', checked: false },
{ id: 3, text: 'Todo 3 HOT', checked: false }
// text has been updated, but does not show in the DOM.
// Also, I get a warning '[react-router] You cannot change
// <Router routes>; it will be ignored'
], action) => {
switch (action.type) {
case 'TOGGLE_TODO':
return state.map(todo => {
if (todo.id === action.id) {
todo.checked = !todo.checked;
}
return todo;
});
default:
return state;
}
};
Issue 2
When the action of a reducer is updated, the function behaviour does get updated in the DOM. However, I get a <Router routes> warning in console.
export default (state = [
{ id: 1, text: 'Todo 1', checked: false },
{ id: 2, text: 'Todo 2', checked: false },
{ id: 3, text: 'Todo 3', checked: false }
], action) => {
switch (action.type) {
case 'TOGGLE_TODO':
return state.map(todo => {
if (todo.id === action.id) {
todo.checked = todo.checked;
// the behaviour of action TOGGLE_TODO is updated.
// However, I get a warning '[react-router] You cannot
// change <Router routes>; it will be ignored'
}
return todo;
});
default:
return state;
}
};
How could I resolve the issues? I wasn't sure if <Router routes> warning has anything to do with Redux, but I thought I would post it here since I could only observe it when hot reloading reducers. To view all the source files, please refer to this tag. Thank you in advance!
I had a similar situation with Redux Provider and found this solution.
The problem is that the Provider doesn't allow replacement of the store with a re-render.
My solution is to unmount the root element and re-render the Provider with the app, only in case of hot reloading.
const rootElement = document.getElementById('root');
let getRouter = router;
const renderApp = () => render(
<Provider store={store}>
<AppContainer>
{getRouter(store, history)}
</AppContainer>
</Provider>,
rootElement
);
if (module.hot) {
// Enable Webpack hot module replacement
module.hot.accept('./router', () => {
getRouter = require('./router').default;
unmountComponentAtNode(rootElement);
renderApp();
});
}
Most helpful comment
I had a similar situation with Redux Provider and found this solution.
The problem is that the Provider doesn't allow replacement of the store with a re-render.
My solution is to unmount the root element and re-render the Provider with the app, only in case of hot reloading.