Skip to content

Commit b6acd36

Browse files
authored
Merge pull request #1144 from cymen/master
Add support for multiple `require.context` with addition of `useContexts`
2 parents c3fbdc0 + 1b739a2 commit b6acd36

File tree

4 files changed

+67
-0
lines changed

4 files changed

+67
-0
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ If you need help upgrading `react-rails`, `webpacker` to `shakapacker`, or JS pa
1212
- Camelizes keys with primitive values, in addition to hashes #946
1313
- Expose alternative implementations for `ReactUJS.getConstructor` #1050
1414
- Include turbolinks in dev and update webdrivers #1174
15+
- Add support for multiple `require.context` with addition of `useContexts` #1144
16+
- Update dependencies
1517

1618
#### Update dependencies
1719
- react to 17.0.2 #1218

README.md

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,24 @@ ReactRailsUJS.useContext(myCustomContext)
173173

174174
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).
175175

176+
In some cases, having multiple `require.context` entries may be desired. Examples of this include:
177+
178+
- Refactoring a typical Rails application into a Rails API with an (eventually) separate Single Page Application (SPA). For this use case, one can add a separate pack in addition to the typical `application` one. React components can be shared between the packs but the new pack can use a minimal Rails view layout, different default styling, etc.
179+
- 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:
180+
181+
```js
182+
// server_rendering.js
183+
var homepageRequireContext = require.context('homepage', true);
184+
var searchRequireContext = require.context('search', true);
185+
var checkoutRequireContext = require.context('checkout', true);
186+
187+
var ReactRailsUJS = require('react_ujs');
188+
ReactRailsUJS.useContexts([
189+
homepageRequireContext,
190+
searchRequireContext,
191+
checkoutRequireContext
192+
]);
193+
```
176194
### File naming
177195

178196
React-Rails supports plenty of file extensions such as: .js, .jsx.js, .js.jsx, .es6.js, .coffee, etcetera!

react_ujs/index.js

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ var detectEvents = require("./src/events/detect")
66
var constructorFromGlobal = require("./src/getConstructor/fromGlobal")
77
var constructorFromRequireContext = require("./src/getConstructor/fromRequireContext")
88
var constructorFromRequireContextWithGlobalFallback = require("./src/getConstructor/fromRequireContextWithGlobalFallback")
9+
var constructorFromRequireContextsWithGlobalFallback = require("./src/getConstructor/fromRequireContextsWithGlobalFallback")
910
const { supportsHydration, reactHydrate, createReactRootLike } = require("./src/renderHelpers")
1011

1112
var ReactRailsUJS = {
@@ -79,6 +80,13 @@ var ReactRailsUJS = {
7980
this.getConstructor = constructorFromRequireContextWithGlobalFallback(requireContext)
8081
},
8182

83+
// Given an array of Webpack `require.context`,
84+
// try finding components with `require`,
85+
// then falling back to global lookup.
86+
useContexts: function(requireContexts) {
87+
this.getConstructor = constructorFromRequireContextsWithGlobalFallback(requireContexts)
88+
},
89+
8290
// Render `componentName` with `props` to a string,
8391
// using the specified `renderFunction` from `react-dom/server`.
8492
serverRender: function(renderFunction, componentName, props) {
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
// Make a function which:
2+
// - First tries to require the name
3+
// - Then falls back to global lookup
4+
var fromGlobal = require("./fromGlobal")
5+
var fromRequireContext = require("./fromRequireContext")
6+
7+
module.exports = function(reqctxs) {
8+
var fromCtxs = reqctxs.map((reqctx) => fromRequireContext(reqctx))
9+
return function(className) {
10+
var component;
11+
try {
12+
var index = 0, fromCtx, firstErr;
13+
do {
14+
fromCtx = fromCtxs[index];
15+
16+
try {
17+
// `require` will raise an error if this className isn't found:
18+
component = fromCtx(className)
19+
} catch (fromCtxErr) {
20+
if (!firstErr) {
21+
firstErr = fromCtxErr;
22+
}
23+
}
24+
25+
index += 1;
26+
} while (index < fromCtxs.length);
27+
if (!component) throw firstErr;
28+
} catch (firstErr) {
29+
// fallback to global:
30+
try {
31+
component = fromGlobal(className)
32+
} catch (secondErr) {
33+
console.error(firstErr)
34+
console.error(secondErr)
35+
}
36+
}
37+
return component
38+
}
39+
}

0 commit comments

Comments
 (0)