Skip to content

Commit b933ed8

Browse files
Judahmeekjustin808
authored andcommitted
Fix HMR (#865)
* works on ReduxApp and ReduxSharedStoreApp * On ReduxSharedStoreApp, only one instance of the component shown twice updates.
1 parent 79a5e4c commit b933ed8

File tree

7 files changed

+841
-650
lines changed

7 files changed

+841
-650
lines changed

spec/dummy/client/.babelrc

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
11
{
2-
"presets": ["es2015", "stage-2", "react"]
2+
"presets": [["es2015", {"modules": false}], "stage-2", "react"],
3+
"plugins": [
4+
"react-hot-loader/babel"
5+
]
36
}

spec/dummy/client/app/startup/ClientReduxApp.jsx

Lines changed: 28 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@
55
import React from 'react';
66
import { combineReducers, applyMiddleware, createStore } from 'redux';
77
import { Provider } from 'react-redux';
8-
import middleware from 'redux-thunk';
8+
import thunkMiddleware from 'redux-thunk';
9+
import { AppContainer } from "react-hot-loader";
10+
import { render } from "react-dom";
911

1012
import reducers from '../reducers/reducersIndex';
1113
import composeInitialState from '../store/composeInitialState';
@@ -18,19 +20,36 @@ import HelloWorldContainer from '../components/HelloWorldContainer';
1820
* React will see that the state is the same and not do anything.
1921
*
2022
*/
21-
export default (props, railsContext) => {
23+
export default (props, railsContext, domNodeId) => {
2224
const combinedReducer = combineReducers(reducers);
2325
const combinedProps = composeInitialState(props, railsContext);
2426

2527
// This is where we'll put in the middleware for the async function. Placeholder.
2628
// store will have helloWorldData as a top level property
27-
const store = applyMiddleware(middleware)(createStore)(combinedReducer, combinedProps);
29+
const store = createStore(combinedReducer, combinedProps, applyMiddleware(thunkMiddleware));
2830

29-
// Provider uses the this.props.children, so we're not typical React syntax.
31+
// renderApp is a function required for hot reloading. see
32+
// https://github.com/retroalgic/react-on-rails-hot-minimal/blob/master/client/src/entry.js
33+
34+
// Provider uses this.props.children, so we're not typical React syntax.
3035
// This allows redux to add additional props to the HelloWorldContainer.
31-
return (
32-
<Provider store={store}>
33-
<HelloWorldContainer />
34-
</Provider>
35-
);
36+
const renderApp = (Komponent) => {
37+
const element = (
38+
<AppContainer>
39+
<Provider store={store}>
40+
<Komponent />
41+
</Provider>
42+
</AppContainer>
43+
)
44+
render(element, document.getElementById(domNodeId));
45+
}
46+
47+
renderApp(HelloWorldContainer);
48+
49+
if (module.hot) {
50+
module.hot.accept(['../reducers/reducersIndex', '../components/HelloWorldContainer'], () => {
51+
store.replaceReducer(combineReducers(reducers));
52+
renderApp(HelloWorldContainer);
53+
})
54+
}
3655
};

spec/dummy/client/app/startup/ClientReduxSharedStoreApp.jsx

Lines changed: 26 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@
44
import React from 'react';
55
import { Provider } from 'react-redux';
66
import ReactOnRails from 'react-on-rails';
7+
import { AppContainer } from "react-hot-loader";
8+
import { render } from "react-dom";
79

810
import HelloWorldContainer from '../components/HelloWorldContainer';
911

@@ -12,13 +14,31 @@ import HelloWorldContainer from '../components/HelloWorldContainer';
1214
* This is used for the client rendering hook after the page html is rendered.
1315
* React will see that the state is the same and not do anything.
1416
*/
15-
export default () => {
17+
export default (props, railsContext, domNodeId) => {
1618
// This is where we get the existing store.
1719
const store = ReactOnRails.getStore('SharedReduxStore');
1820

19-
return (
20-
<Provider store={store}>
21-
<HelloWorldContainer />
22-
</Provider>
23-
);
21+
// renderApp is a function required for hot reloading. see
22+
// https://github.com/retroalgic/react-on-rails-hot-minimal/blob/master/client/src/entry.js
23+
24+
// Provider uses this.props.children, so we're not typical React syntax.
25+
// This allows redux to add additional props to the HelloWorldContainer.
26+
const renderApp = (Komponent) => {
27+
const element = (
28+
<AppContainer>
29+
<Provider store={store}>
30+
<Komponent />
31+
</Provider>
32+
</AppContainer>
33+
)
34+
render(element, document.getElementById(domNodeId));
35+
}
36+
37+
renderApp(HelloWorldContainer);
38+
39+
if (module.hot) {
40+
module.hot.accept(['../components/HelloWorldContainer'], () => {
41+
renderApp(HelloWorldContainer);
42+
})
43+
}
2444
};

spec/dummy/client/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,8 @@
3434
"react": "^15.5.4",
3535
"react-dom": "^15.5.4",
3636
"react-helmet": "^5.0.3",
37-
"react-on-rails": "^8.0.1",
37+
"react-hot-loader": "^3.0.0-beta.6",
38+
"react-on-rails": "8.0.2",
3839
"react-proptypes": "^0.0.1",
3940
"react-redux": "^5.0.4",
4041
"react-router": "3.0.5",
@@ -59,7 +60,6 @@
5960
"eslint-plugin-react": "^6.10.3",
6061
"fbjs": "^0.8.12",
6162
"jsdom": "^9.12.0",
62-
"react-transform-hmr": "^1.0.4",
6363
"tape": "^4.6.3",
6464
"webpack-dev-server": "^2.4.2"
6565
},

spec/dummy/client/server-rails-hot.js

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,10 +13,10 @@
1313
// 2. Make sure you have a hot-assets target in your client/package.json
1414
// 3. Start up `foreman start -f Procfile.hot` to start both Rails and the hot reload server.
1515

16-
import webpack from 'webpack';
17-
import WebpackDevServer from 'webpack-dev-server';
16+
const webpack = require('webpack');
17+
const WebpackDevServer = require('webpack-dev-server');
1818
const { resolve } = require('path');
19-
import webpackConfig from './webpack.client.rails.hot.config';
19+
const webpackConfig = require('./webpack.client.rails.hot.config');
2020

2121
const webpackConfigLoader = require('react-on-rails/webpackConfigLoader');
2222
const configPath = resolve('..', 'config');
@@ -26,6 +26,11 @@ const compiler = webpack(webpackConfig);
2626

2727
const devServer = new WebpackDevServer(compiler, {
2828
contentBase: hotReloadingUrl,
29+
headers: {
30+
'Access-Control-Allow-Origin': '*',
31+
},
32+
disableHostCheck: true,
33+
clientLogLevel: 'info',
2934
hot: true,
3035
inline: true,
3136
historyApiFallback: true,

spec/dummy/client/webpack.client.rails.hot.config.js

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@
33
// cd client && babel-node server-rails-hot.js
44
// Note that Foreman (Procfile.dev) has also been configured to take care of this.
55

6+
// require() is used rather than import because hot reloading with webpack
7+
// requires webpack to transform modules from ES6 to ES5 instead of babel
8+
// and webpack can not transform its own config files.
69
const { resolve } = require('path');
710
const webpack = require('webpack');
811
const merge = require('webpack-merge');
@@ -11,12 +14,19 @@ const webpackConfigLoader = require('react-on-rails/webpackConfigLoader');
1114
const configPath = resolve('..', 'config');
1215
const { hotReloadingUrl, webpackOutputPath } = webpackConfigLoader(configPath);
1316

14-
module.exports = merge(config, {
17+
// entry is prepended because 'react-hot-loader/patch' must be the very first entry
18+
// for hot reloading to work.
19+
module.exports = merge.strategy(
20+
{
21+
entry: 'prepend'
22+
}
23+
)(config, {
1524

1625
devtool: 'eval-source-map',
1726

1827
entry: {
1928
'app-bundle': [
29+
'react-hot-loader/patch',
2030
`webpack-dev-server/client?${hotReloadingUrl}`,
2131
'webpack/hot/only-dev-server'
2232
],
@@ -25,6 +35,7 @@ module.exports = merge(config, {
2535
output: {
2636
filename: '[name].js',
2737
path: webpackOutputPath,
38+
publicPath: `${hotReloadingUrl}/`,
2839
},
2940

3041
module: {
@@ -33,22 +44,6 @@ module.exports = merge(config, {
3344
test: /\.jsx?$/,
3445
loader: 'babel-loader',
3546
exclude: /node_modules/,
36-
query: {
37-
plugins: [
38-
[
39-
'react-transform',
40-
{
41-
transforms: [
42-
{
43-
transform: 'react-transform-hmr',
44-
imports: ['react'],
45-
locals: ['module'],
46-
},
47-
],
48-
},
49-
],
50-
],
51-
},
5247
},
5348
{
5449
test: /\.css$/,
@@ -111,8 +106,13 @@ module.exports = merge(config, {
111106
],
112107
},
113108

109+
// webpack.NamedModulesPlugin() is an optional module that is great for HMR debugging
110+
// since it transform module IDs (112, 698, etc...) into their respective paths,
111+
// but it can conflict with other libraries that expect global references. When in doubt, throw it out.
112+
114113
plugins: [
115114
new webpack.HotModuleReplacementPlugin(),
115+
new webpack.NamedModulesPlugin(),
116116
new webpack.NoEmitOnErrorsPlugin(),
117117
],
118118
});

0 commit comments

Comments
 (0)