From ee3557f4accf7210988dfd52c7d96a5f9f718aff Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 16:29:19 -0600 Subject: [PATCH 1/6] add renderComponent extension point --- react_ujs/index.js | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/react_ujs/index.js b/react_ujs/index.js index 9f56d2b17..f0499e9b7 100644 --- a/react_ujs/index.js +++ b/react_ujs/index.js @@ -80,6 +80,10 @@ var ReactRailsUJS = { return ReactDOMServer[renderFunction](element) }, + renderComponent: function(renderFunction, component, node, props) { + ReactDOM[renderFunction](component, node); + }, + // Within `searchSelector`, find nodes which should have React components // inside them, and mount them with their props. mountComponents: function(searchSelector) { @@ -112,9 +116,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); } } } From 7cb3d152496182c059245cee7be7b820066ffc3d Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 16:36:17 -0600 Subject: [PATCH 2/6] doc + default --- react_ujs/index.js | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/react_ujs/index.js b/react_ujs/index.js index f0499e9b7..cbed2c99b 100644 --- a/react_ujs/index.js +++ b/react_ujs/index.js @@ -80,7 +80,12 @@ var ReactRailsUJS = { return ReactDOMServer[renderFunction](element) }, - renderComponent: function(renderFunction, component, node, props) { + // Render `component` using the specified `renderFunction` from `react-dom`. + // Override this function to render components in a custom way, + // the default is ReactRailsUJS.renderWithReactDOM + renderComponent: this.renderWithReactDOM, + + renderWithReactDOM: function(renderFunction, component, node, props) { ReactDOM[renderFunction](component, node); }, From acdee60bd13d0e71e81ad6a2c03879ab84493088 Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 17:05:47 -0600 Subject: [PATCH 3/6] mirror getConstructor structure --- react_ujs/index.js | 35 +++++++++--- .../src/renderComponent/withHotReload.js | 54 +++++++++++++++++++ react_ujs/src/renderComponent/withReactDOM.js | 8 +++ 3 files changed, 91 insertions(+), 6 deletions(-) create mode 100644 react_ujs/src/renderComponent/withHotReload.js create mode 100644 react_ujs/src/renderComponent/withReactDOM.js diff --git a/react_ujs/index.js b/react_ujs/index.js index cbed2c99b..12065efc1 100644 --- a/react_ujs/index.js +++ b/react_ujs/index.js @@ -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") + var ReactRailsUJS = { // This attribute holds the name of component which should be mounted // example: `data-react-class="MyApp.Items.EditForm"` @@ -81,12 +84,32 @@ var ReactRailsUJS = { }, // Render `component` using the specified `renderFunction` from `react-dom`. - // Override this function to render components in a custom way, - // the default is ReactRailsUJS.renderWithReactDOM - renderComponent: this.renderWithReactDOM, - - renderWithReactDOM: function(renderFunction, component, node, props) { - ReactDOM[renderFunction](component, node); + // Override this function to render components in a custom way. + renderComponent: renderWithReactDOM, + + // Enables hot reload for component rendering. + // + // Ensure + // 1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; + // 2. your webpack config has the following in dev: + // { + // module: { + // rules: [ + // { + // test: /\.(jsx|tsx)?$/, + // use: ["react-hot-loader/webpack"], + // }, + // ], + // }, + // resolve: { + // alias: { + // "react-dom": "@hot-loader/react-dom", + // }, + // }, + // } + // + useHotReload: function(requireContext) { + this.renderComponent = renderWithHotReload(requireContext) }, // Within `searchSelector`, find nodes which should have React components diff --git a/react_ujs/src/renderComponent/withHotReload.js b/react_ujs/src/renderComponent/withHotReload.js new file mode 100644 index 000000000..4dfebe433 --- /dev/null +++ b/react_ujs/src/renderComponent/withHotReload.js @@ -0,0 +1,54 @@ +var ReactDOM = require("react-dom") +var reactHotLoader = require("react-hot-loader") +var AppContainer = reactHotLoader.AppContainer; + +// Render React component with hot reload. +// +// Ensure +// 1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; +// 2. your webpack config has the following in dev: +// { +// module: { +// rules: [ +// { +// test: /\.(jsx|tsx)?$/, +// use: ["react-hot-loader/webpack"], +// }, +// ], +// }, +// resolve: { +// alias: { +// "react-dom": "@hot-loader/react-dom", +// }, +// }, +// } +// +module.exports = function(reqctx) { + return function(renderFunctionName, component, node, props) { + var className = node.getAttribute(ReactRailsUJS.CLASS_NAME_ATTR); + var filename = getFileNameFromClassName(className); + var path = reqctx.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, () => { + var FreshConstructor = ReactRailsUJS.getConstructor(className); + var FreshComponent = React.createElement(FreshConstructor, props); + + ReactDOM[renderFunctionName](React.createElement(AppContainer, null, FreshComponent), node); + }); + + ReactDOM[renderFunctionName](React.createElement(AppContainer, null, component), node); + }; +} + +function getFileNameFromClassName(className) { + var parts = className.split("."); + var filename = parts.shift(); + + return filename; +} diff --git a/react_ujs/src/renderComponent/withReactDOM.js b/react_ujs/src/renderComponent/withReactDOM.js new file mode 100644 index 000000000..bf56a54f9 --- /dev/null +++ b/react_ujs/src/renderComponent/withReactDOM.js @@ -0,0 +1,8 @@ +// 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); +}; From 65439672525bfee0644df72aa6f340e00260781f Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 17:06:23 -0600 Subject: [PATCH 4/6] add missing variable --- react_ujs/src/renderComponent/withReactDOM.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/react_ujs/src/renderComponent/withReactDOM.js b/react_ujs/src/renderComponent/withReactDOM.js index bf56a54f9..a6785a2ed 100644 --- a/react_ujs/src/renderComponent/withReactDOM.js +++ b/react_ujs/src/renderComponent/withReactDOM.js @@ -1,3 +1,5 @@ +var ReactDOM = require("react-dom") + // Render React component via ReactDOM, for example: // // - `renderComponent("hydrate", component, node, props)` -> `ReactDOM.hydrate(component, node);` From bfe05492f9ed8770d13d292be4e63ede5ad216e9 Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 17:11:19 -0600 Subject: [PATCH 5/6] update readme --- README.md | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 425d77c55..a962e1139 100644 --- a/README.md +++ b/README.md @@ -683,7 +683,34 @@ LibV8 itself is already [beyond version 7](https://github.com/cowboyd/libv8/rele ### HMR Hot Module Replacement is [possible with this gem](https://stackoverflow.com/a/54846330/193785) as it does just pass through to Webpacker. Please open an issue to let us know tips and tricks for it to add to the wiki. -Sample repo that shows HMR working with `react-rails`: [https://github.com/edelgado/react-rails-hmr](https://github.com/edelgado/react-rails-hmr) +Similar to `useContext`, you can pass the webpack context into `useHotReload` to enable hot reload: +```js +var myCustomContext = require.context("custom_components", true) +var ReactRailsUJS = require("react_ujs") +// use `custom_components/` for <%= react_component(...) %> calls +ReactRailsUJS.useHotReload(myCustomContext) +``` + +You must ensure +1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; +2. your webpack config has the following in dev: +```js +{ + module: { + rules: [ + { + test: /\.(jsx|tsx)?$/, + use: ["react-hot-loader/webpack"], + }, + ], + }, + resolve: { + alias: { + "react-dom": "@hot-loader/react-dom", + }, + }, +} +``` One caveat is that currently you [cannot Server-Side Render along with HMR](https://github.com/reactjs/react-rails/issues/925#issuecomment-415469572). From ad27aee8ff15734d277d86b20540976759c25027 Mon Sep 17 00:00:00 2001 From: Edmond Chui Date: Sat, 25 Apr 2020 17:18:06 -0600 Subject: [PATCH 6/6] rollback hot reload specfic diffs --- README.md | 29 +--------- react_ujs/index.js | 27 +--------- .../src/renderComponent/withHotReload.js | 54 ------------------- 3 files changed, 2 insertions(+), 108 deletions(-) delete mode 100644 react_ujs/src/renderComponent/withHotReload.js diff --git a/README.md b/README.md index a962e1139..425d77c55 100644 --- a/README.md +++ b/README.md @@ -683,34 +683,7 @@ LibV8 itself is already [beyond version 7](https://github.com/cowboyd/libv8/rele ### HMR Hot Module Replacement is [possible with this gem](https://stackoverflow.com/a/54846330/193785) as it does just pass through to Webpacker. Please open an issue to let us know tips and tricks for it to add to the wiki. -Similar to `useContext`, you can pass the webpack context into `useHotReload` to enable hot reload: -```js -var myCustomContext = require.context("custom_components", true) -var ReactRailsUJS = require("react_ujs") -// use `custom_components/` for <%= react_component(...) %> calls -ReactRailsUJS.useHotReload(myCustomContext) -``` - -You must ensure -1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; -2. your webpack config has the following in dev: -```js -{ - module: { - rules: [ - { - test: /\.(jsx|tsx)?$/, - use: ["react-hot-loader/webpack"], - }, - ], - }, - resolve: { - alias: { - "react-dom": "@hot-loader/react-dom", - }, - }, -} -``` +Sample repo that shows HMR working with `react-rails`: [https://github.com/edelgado/react-rails-hmr](https://github.com/edelgado/react-rails-hmr) One caveat is that currently you [cannot Server-Side Render along with HMR](https://github.com/reactjs/react-rails/issues/925#issuecomment-415469572). diff --git a/react_ujs/index.js b/react_ujs/index.js index 12065efc1..0ef70a5a3 100644 --- a/react_ujs/index.js +++ b/react_ujs/index.js @@ -7,7 +7,6 @@ var constructorFromGlobal = require("./src/getConstructor/fromGlobal") var constructorFromRequireContextWithGlobalFallback = require("./src/getConstructor/fromRequireContextWithGlobalFallback") var renderWithReactDOM = require("./src/renderComponent/withReactDOM") -var renderWithHotReload = require("./src/renderComponent/withHotReload") var ReactRailsUJS = { // This attribute holds the name of component which should be mounted @@ -85,33 +84,9 @@ var ReactRailsUJS = { // 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. - // - // Ensure - // 1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; - // 2. your webpack config has the following in dev: - // { - // module: { - // rules: [ - // { - // test: /\.(jsx|tsx)?$/, - // use: ["react-hot-loader/webpack"], - // }, - // ], - // }, - // resolve: { - // alias: { - // "react-dom": "@hot-loader/react-dom", - // }, - // }, - // } - // - useHotReload: function(requireContext) { - this.renderComponent = renderWithHotReload(requireContext) - }, - // Within `searchSelector`, find nodes which should have React components // inside them, and mount them with their props. mountComponents: function(searchSelector) { diff --git a/react_ujs/src/renderComponent/withHotReload.js b/react_ujs/src/renderComponent/withHotReload.js deleted file mode 100644 index 4dfebe433..000000000 --- a/react_ujs/src/renderComponent/withHotReload.js +++ /dev/null @@ -1,54 +0,0 @@ -var ReactDOM = require("react-dom") -var reactHotLoader = require("react-hot-loader") -var AppContainer = reactHotLoader.AppContainer; - -// Render React component with hot reload. -// -// Ensure -// 1. [react-hot-loader](https://github.com/gaearon/react-hot-loader) and [@hot-loader/react-dom](https://github.com/hot-loader/react-dom) are installed; -// 2. your webpack config has the following in dev: -// { -// module: { -// rules: [ -// { -// test: /\.(jsx|tsx)?$/, -// use: ["react-hot-loader/webpack"], -// }, -// ], -// }, -// resolve: { -// alias: { -// "react-dom": "@hot-loader/react-dom", -// }, -// }, -// } -// -module.exports = function(reqctx) { - return function(renderFunctionName, component, node, props) { - var className = node.getAttribute(ReactRailsUJS.CLASS_NAME_ATTR); - var filename = getFileNameFromClassName(className); - var path = reqctx.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, () => { - var FreshConstructor = ReactRailsUJS.getConstructor(className); - var FreshComponent = React.createElement(FreshConstructor, props); - - ReactDOM[renderFunctionName](React.createElement(AppContainer, null, FreshComponent), node); - }); - - ReactDOM[renderFunctionName](React.createElement(AppContainer, null, component), node); - }; -} - -function getFileNameFromClassName(className) { - var parts = className.split("."); - var filename = parts.shift(); - - return filename; -}