From 7e5f0d8086472388e8dc3b6e23c40cf87e74ddf1 Mon Sep 17 00:00:00 2001 From: Riccardo Margiotta Date: Mon, 28 Nov 2022 12:01:33 +0000 Subject: [PATCH 1/5] add useContexts which takes an array of require.context. Authored by @cymen. --- react_ujs/index.js | 8 ++++ .../fromRequireContextsWithGlobalFallback.js | 41 +++++++++++++++++++ 2 files changed, 49 insertions(+) create mode 100644 react_ujs/src/getConstructor/fromRequireContextsWithGlobalFallback.js diff --git a/react_ujs/index.js b/react_ujs/index.js index 4c4c991fb..6b8effc85 100644 --- a/react_ujs/index.js +++ b/react_ujs/index.js @@ -6,6 +6,7 @@ var detectEvents = require("./src/events/detect") var constructorFromGlobal = require("./src/getConstructor/fromGlobal") var constructorFromRequireContext = require("./src/getConstructor/fromRequireContext") var constructorFromRequireContextWithGlobalFallback = require("./src/getConstructor/fromRequireContextWithGlobalFallback") +var constructorFromRequireContextsWithGlobalFallback = require("./src/getConstructor/fromRequireContextsWithGlobalFallback") const { supportsHydration, reactHydrate, createReactRootLike } = require("./src/renderHelpers") var ReactRailsUJS = { @@ -79,6 +80,13 @@ var ReactRailsUJS = { this.getConstructor = constructorFromRequireContextWithGlobalFallback(requireContext) }, + // Given an array of Webpack `require.context`, + // try finding components with `require`, + // then falling back to global lookup. + useContexts: function(requireContexts) { + this.getConstructor = constructorFromRequireContextsWithGlobalFallback(requireContexts) + }, + // Render `componentName` with `props` to a string, // using the specified `renderFunction` from `react-dom/server`. serverRender: function(renderFunction, componentName, props) { diff --git a/react_ujs/src/getConstructor/fromRequireContextsWithGlobalFallback.js b/react_ujs/src/getConstructor/fromRequireContextsWithGlobalFallback.js new file mode 100644 index 000000000..bcf525fec --- /dev/null +++ b/react_ujs/src/getConstructor/fromRequireContextsWithGlobalFallback.js @@ -0,0 +1,41 @@ +// Make a function which: +// - First tries to require the name +// - Then falls back to global lookup +var fromGlobal = require("./fromGlobal"); +var fromRequireContext = require("./fromRequireContext"); + +module.exports = function (reqctxs) { + var fromCtxs = reqctxs.map((reqctx) => fromRequireContext(reqctx)); + return function (className) { + var component; + try { + var index = 0, + fromCtx, + firstErr; + do { + fromCtx = fromCtxs[index]; + + try { + // `require` will raise an error if this className isn't found: + component = fromCtx(className); + } catch (fromCtxErr) { + if (!firstErr) { + firstErr = fromCtxErr; + } + } + + index += 1; + } while (index < fromCtxs.length); + if (!component) throw firstErr; + } catch (firstErr) { + // fallback to global: + try { + component = fromGlobal(className); + } catch (secondErr) { + console.error(firstErr); + console.error(secondErr); + } + } + return component; + }; +}; From fb2c8a8692c32668fefcc0eb6bccae642de68c3a Mon Sep 17 00:00:00 2001 From: Riccardo Margiotta Date: Mon, 28 Nov 2022 12:04:59 +0000 Subject: [PATCH 2/5] Update README to include information about useContexts. --- README.md | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/README.md b/README.md index 77c3f7b87..75bab3500 100644 --- a/README.md +++ b/README.md @@ -172,6 +172,22 @@ ReactRailsUJS.useContext(myCustomContext) If `require` fails to find your component, [`ReactRailsUJS`](#ujs) falls back to the global namespace, described in [Use with Asset Pipeline](#use-with-asset-pipeline). +In some cases, having multiple `require.context` may be desired - for example, if you have additional entry points to create JavaScript files for different routes, you would add multiple `require.context` to your `server_rendering.js` to allow for [Server-Side Rendering](#server-side-rendering) across your application. If so, an array of `require.context` can be passed to `ReactRailsUJS.useContexts`. With an array of contexts, an attempt will be made to `require` the component from each context before falling back to the global namespace as described above. + +```js +// server_rendering.js +var componentsRequireContext = require.context('components', true); +var searchRequireContext = require.context('search', true); +var checkoutRequireContext = require.context('checkout', true); + +var ReactRailsUJS = require('react_ujs'); +ReactRailsUJS.useContexts([ + componentsRequireContext, + searchRequireContext, + checkoutRequireContext +]); +``` + ### File naming React-Rails supports plenty of file extensions such as: .js, .jsx.js, .js.jsx, .es6.js, .coffee, etcetera! From 932dd09ac7efe808e9705969d9df8235e96109d6 Mon Sep 17 00:00:00 2001 From: Riccardo Margiotta Date: Mon, 28 Nov 2022 12:09:41 +0000 Subject: [PATCH 3/5] Update changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 192eef6e9..a6ffe2d38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,7 @@ #### New Features - Camelizes keys with primitive values, in addition to hashes #946 - Expose alternative implementations for `ReactUJS.getConstructor` #1050 +- Add support for multiple `require.context` with addition of `useContexts` #1221 - Update dependencies - react to 17.0.2 #1218 - webpack to 5.74.0 From 62f35c6208f30a1aca8f02c07faf075104f10def Mon Sep 17 00:00:00 2001 From: Riccardo Margiotta Date: Mon, 28 Nov 2022 18:36:19 +0000 Subject: [PATCH 4/5] Update usage example. --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 75bab3500..32065f92b 100644 --- a/README.md +++ b/README.md @@ -176,13 +176,13 @@ In some cases, having multiple `require.context` may be desired - for example, i ```js // server_rendering.js -var componentsRequireContext = require.context('components', true); +var applicationRequireContext = require.context('application', true); var searchRequireContext = require.context('search', true); var checkoutRequireContext = require.context('checkout', true); var ReactRailsUJS = require('react_ujs'); ReactRailsUJS.useContexts([ - componentsRequireContext, + applicationRequireContext, searchRequireContext, checkoutRequireContext ]); From 88130eade0cbc19aefca0455acfa45a6d0a1071d Mon Sep 17 00:00:00 2001 From: Riccardo Margiotta Date: Mon, 28 Nov 2022 18:50:12 +0000 Subject: [PATCH 5/5] Update usage example to better explain route-splitting and SSR. --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 32065f92b..9aa08ed99 100644 --- a/README.md +++ b/README.md @@ -172,17 +172,17 @@ ReactRailsUJS.useContext(myCustomContext) If `require` fails to find your component, [`ReactRailsUJS`](#ujs) falls back to the global namespace, described in [Use with Asset Pipeline](#use-with-asset-pipeline). -In some cases, having multiple `require.context` may be desired - for example, if you have additional entry points to create JavaScript files for different routes, you would add multiple `require.context` to your `server_rendering.js` to allow for [Server-Side Rendering](#server-side-rendering) across your application. If so, an array of `require.context` can be passed to `ReactRailsUJS.useContexts`. With an array of contexts, an attempt will be made to `require` the component from each context before falling back to the global namespace as described above. +In some cases, having multiple `require.context` entries may be desired. In a larger application, you might find it helpful to split your JavaScript by routes/controllers to avoid serving unused components and improve your site performance by keeping bundles smaller. For example, you might have separate bundles for homepage, search, and checkout routes. In that scenario, you can add an array of `require.context` component directory paths via `useContexts` to `server_rendering.js`, to allow for [Server-Side Rendering](#server-side-rendering) across your application ```js // server_rendering.js -var applicationRequireContext = require.context('application', true); +var homepageRequireContext = require.context('homepage', true); var searchRequireContext = require.context('search', true); var checkoutRequireContext = require.context('checkout', true); var ReactRailsUJS = require('react_ujs'); ReactRailsUJS.useContexts([ - applicationRequireContext, + homepageRequireContext, searchRequireContext, checkoutRequireContext ]);