-
Notifications
You must be signed in to change notification settings - Fork 751
add auto hot reload support #1065
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
ee3557f
7cb3d15
acdee60
6543967
bfe0549
ad27aee
304fa1e
210f835
6537709
5c4cf92
4a71138
bff3b07
dbc43fd
01eb0f0
1741e73
02f7217
087211e
6963eaf
fffc5e8
a53b327
5d06769
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -6,6 +6,9 @@ var detectEvents = require("./src/events/detect") | |
| var constructorFromGlobal = require("./src/getConstructor/fromGlobal") | ||
| var constructorFromRequireContextWithGlobalFallback = require("./src/getConstructor/fromRequireContextWithGlobalFallback") | ||
|
|
||
| var renderWithReactDOM = require("./src/renderComponent/withReactDOM") | ||
| var renderWithHotReload = require("./src/renderComponent/withHotReload") | ||
|
Comment on lines
+9
to
+10
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As mentioned in the add render component extension point PR, the folder structure and import pattern is taking reference from getConstructor for consistency. Please let me know if there is a better way |
||
|
|
||
| var ReactRailsUJS = { | ||
| // This attribute holds the name of component which should be mounted | ||
| // example: `data-react-class="MyApp.Items.EditForm"` | ||
|
|
@@ -71,6 +74,11 @@ var ReactRailsUJS = { | |
| useContext: function(requireContext) { | ||
| this.getConstructor = constructorFromRequireContextWithGlobalFallback(requireContext) | ||
| }, | ||
|
|
||
| // Called after React unmounts component at `node` | ||
| // Override this function to perform any cleanup | ||
| // the default function does nothing | ||
| onComponentUnmountAtNode: function (node) {}, | ||
|
|
||
| // Render `componentName` with `props` to a string, | ||
| // using the specified `renderFunction` from `react-dom/server`. | ||
|
|
@@ -80,6 +88,18 @@ var ReactRailsUJS = { | |
| return ReactDOMServer[renderFunction](element) | ||
| }, | ||
|
|
||
| // Render `component` using the specified `renderFunction` from `react-dom`. | ||
| // Override this function to render components in a custom way. | ||
| // function signature: ("hydrate" | "render", component, node, props) | ||
| renderComponent: renderWithReactDOM, | ||
|
|
||
| // Enables hot reload for component rendering. | ||
| // | ||
| // See the HMR section in README to ensure required steps are completed. | ||
| useHotReload: function(requireContext) { | ||
| this.renderComponent = renderWithHotReload(requireContext) | ||
| }, | ||
|
Comment on lines
+96
to
+101
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let me know if this comment could be more helpful, i.e. should the steps to setup be inlined here?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In addition, there is an opportunity to "stack" instead of "replace". See the example and discussion at #1064. The proposed approach here mirrors |
||
|
|
||
| // Within `searchSelector`, find nodes which should have React components | ||
| // inside them, and mount them with their props. | ||
| mountComponents: function(searchSelector) { | ||
|
|
@@ -112,9 +132,9 @@ var ReactRailsUJS = { | |
| } | ||
|
|
||
| if (hydrate && typeof ReactDOM.hydrate === "function") { | ||
| component = ReactDOM.hydrate(component, node); | ||
| renderComponent("hydrate", component, node, props); | ||
| } else { | ||
| component = ReactDOM.render(component, node); | ||
| renderComponent("render", component, node, props); | ||
| } | ||
| } | ||
| } | ||
|
|
@@ -128,6 +148,7 @@ var ReactRailsUJS = { | |
| for (var i = 0; i < nodes.length; ++i) { | ||
| var node = nodes[i]; | ||
| ReactDOM.unmountComponentAtNode(node); | ||
| ReactRailsUJS.onComponentUnmountAtNode(node); | ||
| } | ||
| }, | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,64 @@ | ||
| var ReactDOM = require("react-dom") | ||
| var reactHotLoader = require("react-hot-loader") | ||
| var AppContainer = reactHotLoader.AppContainer; | ||
|
|
||
| var IS_MOUNTED_ATTR = "data-react-is-mounted"; | ||
|
|
||
| // Render React component with hot reload. | ||
| // | ||
| // See the HMR section in README to ensure required steps are completed. | ||
|
Comment on lines
+7
to
+9
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same for this one. Let me know if this comment could be more helpful, i.e. should the steps to setup be inlined here? |
||
| module.exports = function(webpackRequireContext) { | ||
| ReactRailsUJS.onComponentUnmountAtNode = function(node) { | ||
| node.setAttribute(IS_MOUNTED_ATTR, "false"); | ||
| } | ||
|
|
||
| return function(renderFunctionName, component, node, props) { | ||
| var className = node.getAttribute(ReactRailsUJS.CLASS_NAME_ATTR); | ||
| var filename = getFileNameFromClassName(className); | ||
| var path = webpackRequireContext.resolve("./" + filename); | ||
| var cache = require.cache; | ||
| var module = cache[path]; | ||
| var moduleParent = module && cache[module.parents[0]]; | ||
| if (!moduleParent || !moduleParent.hot) { | ||
| console.warn(`Cannot hot reload for ${path}. Ensure webpack-dev-server is started with --hot and WEBPACKER_DEV_SERVER_HMR=true`); | ||
| return; | ||
| } | ||
| moduleParent.hot.accept(path, () => reRenderAllNodes(className, renderFunctionName)); | ||
|
|
||
| ReactDOM[renderFunctionName](React.createElement(AppContainer, null, component), node); | ||
| node.setAttribute(IS_MOUNTED_ATTR, "true"); | ||
| }; | ||
| } | ||
|
|
||
| function getFileNameFromClassName(className) { | ||
| var parts = className.split("."); | ||
| var filename = parts.shift(); | ||
|
|
||
| return filename; | ||
| } | ||
|
|
||
| function reRenderAllNodes(className, renderFunctionName) { | ||
| var nodes = findAllReactNodes(className); | ||
| for (var i = 0; i < nodes.length; ++i) { | ||
| var node = nodes[i]; | ||
| if (!isReactMountedAtNode(node)) continue; | ||
|
|
||
| var propsJson = node.getAttribute(ujs.PROPS_ATTR); | ||
| var props = propsJson && JSON.parse(propsJson); | ||
| var FreshComponent = React.createElement(FreshConstructor, props); | ||
| ReactDOM[renderFunctionName](React.createElement(AppContainer, null, FreshComponent), node); | ||
| } | ||
| } | ||
|
|
||
| function findAllReactNodes(className) { | ||
| var selector = '[' + ReactRailsUJS.CLASS_NAME_ATTR + '="' + className + '"]'; | ||
| if (ReactRailsUJS.jQuery) { | ||
| return ReactRailsUJS.jQuery(selector, document); | ||
| } else { | ||
| return parent.querySelectorAll(selector); | ||
| } | ||
| } | ||
|
|
||
| function isReactMountedAtNode(node) { | ||
| return node.matches('[' + IS_MOUNTED_ATTR + '="true"]'); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,10 @@ | ||
| var ReactDOM = require("react-dom") | ||
|
|
||
| // Render React component via ReactDOM, for example: | ||
| // | ||
| // - `renderComponent("hydrate", component, node, props)` -> `ReactDOM.hydrate(component, node);` | ||
| // - `renderComponent("render", component, node, props)` -> `ReactDOM.render(component, node);` | ||
| // | ||
| module.exports = function(renderFunctionName, component, node) { | ||
| ReactDOM[renderFunctionName](component, node); | ||
| }; |
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let me know if the steps in readme is concise and simple to follow. Happy to pull into a separate file if requested