Reputation: 2285
I use React + Redux + Webpack + WebpackDevserver. Once the hot loader is launched all my reducers are reseted to the initial state.
Can I keep somehow my reducers in the actual state?
My Webpack config contains:
entry: [
"./index.jsx"
],
output: {
filename: "./bundle.js"
},
module: {
loaders: [
{
test: /\.js|\.jsx$/,
exclude: /node_modules/,
loaders: ["react-hot","babel-loader"],
}
]
},
plugins: [
new webpack.HotModuleReplacementPlugin()
]
My reducers stats with:
const initialState = {
...
}
export default function config(state = initialState, action) { ...
I start my Webpack Dev-Server just by:
"start": "webpack-dev-server",
Upvotes: 25
Views: 17675
Reputation: 26873
Assuming Babel 6, you need to do something along this:
import {createStore} from 'redux';
import rootReducer from '../reducers';
export default function configureStore(initialState) {
const store = createStore(rootReducer, initialState);
if(module.hot) {
// Enable Webpack hot module replacement for reducers
module.hot.accept('../reducers', () => {
const nextReducer = require('../reducers/index').default;
store.replaceReducer(nextReducer);
});
}
return store;
}
You can see the approach in action at my redux demo.
Upvotes: 21
Reputation: 4580
Yes you can, actually if you use react-hot-loader you'll have that exactly result and much more.
react-hot-loader
will make sure to only update the exactly three you had change, and also will keeps the state on redux.
Example: you're creating a modal that receives some info from API and then keeps the info on redux, if you change the text color you know you'll have to wait for the entire app refresh, then the browser render, then you've to open the modal, wait the API and etc. BUT with react-hot-loader, after you changed that color, your modal will be still open, with your current data, and just that color will be updated.
Following the steps from the package README:
1 - You need to install the package (yes doesn't need to be a dev dependency, the README explain with more details why)
npm install react-hot-loader
2 - Add it on .babelrc
// .babelrc
{
"plugins": ["react-hot-loader/babel"]
}
3 - Import hot
at the first line of App.js
file (yes, above React, ReactDOM, above all) and then mark your root component as hot-exported
// App.js
import { hot } from 'react-hot-loader/root';
const App = () => <div>Hello World!</div>;
export default hot(App);
That's it, now you should have a hot reload that focus only at the last changes and keeps the redux state for you.
OBS: If you use hooks, please check out more details here on the docs
Upvotes: 0
Reputation: 4411
Just shape your code like below where you render the app in the root element.
store.js:
export const store = createStore(rootReducer, integrateDevTools)
index.jsx:
This will do the trick.
ReactDOM.render(
<Provider store={store}>
<App />
</Provider>,
document.getElementById('root'),
)
if (module.hot) {
module.hot.accept()
}
Upvotes: 2
Reputation: 1722
Check the code related to store creation - createStore()
.
The store must be built outside app.js
, otherwise, ot will be FLUSHED each time on each HMR update.
Wrong:
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { hot } from 'react-hot-loader';
import { Provider } from 'react-redux';
import { PersistGate } from 'redux-persist/lib/integration/react';
import { AppWidget } from 'containers';
import createStore from 'store/create-store';
const { store, persistor } = createStore(); // <!--- NEW STORE ON HMR, BUG
const rootElement = window.document.getElementById('appWidget');
const render = (Component) => {
ReactDOM.render(
<Provider store={store}>
<PersistGate loading={null} persistor={persistor}>
<Component />
</PersistGate>
</Provider>,
rootElement,
);
};
render(process.env.NODE_ENV === 'development' ? hot(module)(AppWidget) : AppWidget);
Correct:
// app.js
import React from 'react';
import ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { AppWidget } from 'containers';
import store from 'store/create-store';
import { AppContainer } from 'react-hot-loader';
const rootElement = window.document.getElementById('appWidget');
const render = (Component) => {
ReactDOM.render(
<AppContainer>
<Provider store={store}>
<Component />
</Provider>
</AppContainer>,
rootElement,
);
};
render(AppWidget);
if (module.hot) {
module.hot.accept();
}
// create-store.js
import { applyMiddleware, compose, createStore } from 'redux';
import createSagaMiddleware from 'redux-saga';
import thunk from 'redux-thunk';
import rootReducer from './reducers';
import rootSaga from './sagas';
const doCreateStore = () => {
const sagaMiddleware = createSagaMiddleware();
const middleware = [
thunk,
sagaMiddleware,
];
const store = createStore(
rootReducer,
compose(
applyMiddleware(...middleware),
),
);
sagaMiddleware.run(rootSaga);
return store;
};
export default doCreateStore(); // <!-- CREATE AND RETURN STORE, NO BUG
Upvotes: 5