Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 18a4f5b

Browse files
Merge pull request #693 from reactjs/fix-turbolinks-load-order
fix(UJS) support cases when Turbolinks is loaded after UJS
2 parents 0fdcf3d + bac14a5 commit 18a4f5b

File tree

13 files changed

+304
-61
lines changed

13 files changed

+304
-61
lines changed

‎README.md

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -245,7 +245,14 @@ You can use this when the DOM is modified by AJAX calls or modal windows.
245245

246246
`ReactRailsUJS` will automatically mount components on `<%= react_component(...) %>` tags and unmount them when appropriate.
247247

248-
Be sure to load `react_ujs` _after_ these libraries so that it can detect them.
248+
If you need to re-detect events, you can call `detectEvents`:
249+
250+
```js
251+
// Remove previous event handlers and add new ones:
252+
ReactRailsUJS.detectEvents()
253+
```
254+
255+
For example, if `Turbolinks` is loaded _after_ `ReactRailsUJS`, you'll need to call this again. This function removes previous handlers before adding new ones, so it's safe to call as often as needed.
249256

250257
### `getConstructor`
251258

‎lib/assets/javascripts/react_ujs.js

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,37 @@ var turbolinksClassicEvents = __webpack_require__(10)
117117
// see what things are globally available
118118
// and setup event handlers to those things
119119
module.exports = function(ujs) {
120+
121+
if (ujs.handleEvent) {
122+
// We're calling this a second time -- remove previous handlers
123+
turbolinksClassicEvents.teardown(ujs)
124+
turbolinksEvents.teardown(ujs);
125+
turbolinksClassicDeprecatedEvents.teardown(ujs);
126+
pjaxEvents.teardown(ujs);
127+
nativeEvents.teardown(ujs);
128+
}
129+
120130
if (ujs.jQuery) {
121131
ujs.handleEvent = function(eventName, callback) {
122132
ujs.jQuery(document).on(eventName, callback);
123133
};
124-
} else {
134+
ujs.removeEvent = function(eventName, callback) {
135+
ujs.jQuery(document).off(eventName, callback);
136+
}
137+
} else if ('addEventListener' in window) {
125138
ujs.handleEvent = function(eventName, callback) {
126139
document.addEventListener(eventName, callback);
127140
};
141+
ujs.removeEvent = function(eventName, callback) {
142+
document.removeEventListener(eventName, callback);
143+
};
144+
} else {
145+
ujs.handleEvent = function(eventName, callback) {
146+
window.attachEvent(eventName, callback);
147+
};
148+
ujs.removeEvent = function(eventName, callback) {
149+
window.detachEvent(eventName, callback);
150+
};
128151
}
129152

130153
// Detect which kind of events to set up:
@@ -251,8 +274,11 @@ var ReactRailsUJS = {
251274
// the default is ReactRailsUJS.ComponentGlobal
252275
getConstructor: constructorFromGlobal,
253276

254-
useContext: function(req) {
255-
this.getConstructor = constructorFromRequireContextWithGlobalFallback(req)
277+
// Given a Webpack `require.context`,
278+
// try finding components with `require`,
279+
// then falling back to global lookup.
280+
useContext: function(requireContext) {
281+
this.getConstructor = constructorFromRequireContextWithGlobalFallback(requireContext)
256282
},
257283

258284
// Render `componentName` with `props` to a string,
@@ -298,11 +324,36 @@ var ReactRailsUJS = {
298324
ReactDOM.unmountComponentAtNode(node);
299325
}
300326
},
327+
328+
// Check the global context for installed libraries
329+
// and figure out which library to hook up to (pjax, Turbolinks, jQuery)
330+
// This is called on load, but you can call it again if needed
331+
// (It will unmount itself)
332+
detectEvents: function() {
333+
detectEvents(this)
334+
},
335+
}
336+
337+
// These stable references are so that handlers can be added and removed:
338+
ReactRailsUJS.handleMount = function(e) {
339+
var target = undefined;
340+
if (e && e.target) {
341+
target = e.target;
342+
}
343+
ReactRailsUJS.mountComponents(target);
344+
}
345+
ReactRailsUJS.handleUnmount = function(e) {
346+
var target = undefined;
347+
if (e && e.target) {
348+
target = e.target;
349+
}
350+
ReactRailsUJS.unmountComponents(target);
301351
}
302352

353+
303354
if (typeof window !== "undefined") {
304355
// Only setup events for browser (not server-rendering)
305-
detectEvents(ReactRailsUJS)
356+
ReactRailsUJS.detectEvents()
306357
}
307358

308359
// It's a bit of a no-no to populate the global namespace,
@@ -324,12 +375,22 @@ module.exports = {
324375
setup: function(ujs) {
325376
if (ujs.jQuery) {
326377
// Use jQuery if it's present:
327-
ujs.jQuery(function(){ujs.mountComponents()});
378+
ujs.handleEvent("ready",ujs.handleMount);
328379
} else if ('addEventListener' in window) {
329-
document.addEventListener('DOMContentLoaded', function(){ujs.mountComponents()});
380+
ujs.handleEvent('DOMContentLoaded', ujs.handleMount);
330381
} else {
331382
// add support to IE8 without jQuery
332-
window.attachEvent('onload', function() { ujs.mountComponents() });
383+
ujs.handleEvent('onload', ujs.handleMount);
384+
}
385+
},
386+
387+
teardown: function(ujs) {
388+
if (ujs.jQuery) {
389+
ujs.removeEvent("ready", ujs.handleMount);
390+
} else if ('addEventListener' in window) {
391+
ujs.removeEvent('DOMContentLoaded', ujs.handleMount);
392+
} else {
393+
ujs.removeEvent('onload', ujs.handleMount);
333394
}
334395
}
335396
}
@@ -342,10 +403,16 @@ module.exports = {
342403
module.exports = {
343404
// pjax support
344405
setup: function(ujs) {
345-
ujs.handleEvent('ready', function() { ujs.mountComponents() });
346-
ujs.handleEvent('pjax:end', function(e) { ujs.mountComponents(e.target) });
347-
ujs.handleEvent('pjax:beforeReplace', function(e) { ujs.unmountComponents(e.target) });
348-
}
406+
ujs.handleEvent('ready', ujs.handleMount);
407+
ujs.handleEvent('pjax:end', ujs.handleMount);
408+
ujs.handleEvent('pjax:beforeReplace', ujs.handleUnmount);
409+
},
410+
411+
teardown: function() {
412+
ujs.removeEvent('ready', ujs.handleMount);
413+
ujs.removeEvent('pjax:end', ujs.handleMount);
414+
ujs.removeEvent('pjax:beforeReplace', ujs.handleUnmount);
415+
},
349416
}
350417

351418

@@ -356,9 +423,15 @@ module.exports = {
356423
module.exports = {
357424
// Turbolinks 5+ got rid of named events (?!)
358425
setup: function(ujs) {
359-
ujs.handleEvent('DOMContentLoaded', function() { ujs.mountComponents() })
360-
ujs.handleEvent('turbolinks:render', function() { ujs.mountComponents() })
361-
ujs.handleEvent('turbolinks:before-render', function() { ujs.unmountComponents() })
426+
ujs.handleEvent('DOMContentLoaded', ujs.handleMount)
427+
ujs.handleEvent('turbolinks:render', ujs.handleMount)
428+
ujs.handleEvent('turbolinks:before-render', ujs.handleUnmount)
429+
},
430+
431+
teardown: function(ujs) {
432+
ujs.removeEvent('DOMContentLoaded', ujs.handleMount)
433+
ujs.removeEvent('turbolinks:render', ujs.handleMount)
434+
ujs.removeEvent('turbolinks:before-render', ujs.handleUnmount)
362435
},
363436
}
364437

@@ -371,8 +444,12 @@ module.exports = {
371444
// Attach handlers to Turbolinks-Classic events
372445
// for mounting and unmounting components
373446
setup: function(ujs) {
374-
ujs.handleEvent(Turbolinks.EVENTS.CHANGE, function() { ujs.mountComponents() });
375-
ujs.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, function() { ujs.unmountComponents() });
447+
ujs.handleEvent(Turbolinks.EVENTS.CHANGE, ujs.handleMount);
448+
ujs.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, ujs.handleUnmount);
449+
},
450+
teardown: function(ujs) {
451+
ujs.removeEvent(Turbolinks.EVENTS.CHANGE, ujs.handleMount);
452+
ujs.removeEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, ujs.handleUnmount);
376453
}
377454
}
378455

@@ -388,8 +465,12 @@ module.exports = {
388465
// https://github.com/reactjs/react-rails/issues/87
389466
setup: function(ujs) {
390467
Turbolinks.pagesCached(0)
391-
ujs.handleEvent('page:change', function() { ujs.mountComponents() });
392-
ujs.handleEvent('page:receive', function() { ujs.unmountComponents() });
468+
ujs.handleEvent('page:change', ujs.handleMount);
469+
ujs.handleEvent('page:receive', ujs.handleUnmount);
470+
},
471+
teardown: function(ujs) {
472+
ujs.removeEvent('page:change', ujs.handleMount);
473+
ujs.removeEvent('page:receive', ujs.handleUnmount);
393474
}
394475
}
395476

‎react_ujs/dist/react_ujs.js

Lines changed: 99 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -117,14 +117,37 @@ var turbolinksClassicEvents = __webpack_require__(10)
117117
// see what things are globally available
118118
// and setup event handlers to those things
119119
module.exports = function(ujs) {
120+
121+
if (ujs.handleEvent) {
122+
// We're calling this a second time -- remove previous handlers
123+
turbolinksClassicEvents.teardown(ujs)
124+
turbolinksEvents.teardown(ujs);
125+
turbolinksClassicDeprecatedEvents.teardown(ujs);
126+
pjaxEvents.teardown(ujs);
127+
nativeEvents.teardown(ujs);
128+
}
129+
120130
if (ujs.jQuery) {
121131
ujs.handleEvent = function(eventName, callback) {
122132
ujs.jQuery(document).on(eventName, callback);
123133
};
124-
} else {
134+
ujs.removeEvent = function(eventName, callback) {
135+
ujs.jQuery(document).off(eventName, callback);
136+
}
137+
} else if ('addEventListener' in window) {
125138
ujs.handleEvent = function(eventName, callback) {
126139
document.addEventListener(eventName, callback);
127140
};
141+
ujs.removeEvent = function(eventName, callback) {
142+
document.removeEventListener(eventName, callback);
143+
};
144+
} else {
145+
ujs.handleEvent = function(eventName, callback) {
146+
window.attachEvent(eventName, callback);
147+
};
148+
ujs.removeEvent = function(eventName, callback) {
149+
window.detachEvent(eventName, callback);
150+
};
128151
}
129152

130153
// Detect which kind of events to set up:
@@ -251,8 +274,11 @@ var ReactRailsUJS = {
251274
// the default is ReactRailsUJS.ComponentGlobal
252275
getConstructor: constructorFromGlobal,
253276

254-
useContext: function(req) {
255-
this.getConstructor = constructorFromRequireContextWithGlobalFallback(req)
277+
// Given a Webpack `require.context`,
278+
// try finding components with `require`,
279+
// then falling back to global lookup.
280+
useContext: function(requireContext) {
281+
this.getConstructor = constructorFromRequireContextWithGlobalFallback(requireContext)
256282
},
257283

258284
// Render `componentName` with `props` to a string,
@@ -298,11 +324,36 @@ var ReactRailsUJS = {
298324
ReactDOM.unmountComponentAtNode(node);
299325
}
300326
},
327+
328+
// Check the global context for installed libraries
329+
// and figure out which library to hook up to (pjax, Turbolinks, jQuery)
330+
// This is called on load, but you can call it again if needed
331+
// (It will unmount itself)
332+
detectEvents: function() {
333+
detectEvents(this)
334+
},
335+
}
336+
337+
// These stable references are so that handlers can be added and removed:
338+
ReactRailsUJS.handleMount = function(e) {
339+
var target = undefined;
340+
if (e && e.target) {
341+
target = e.target;
342+
}
343+
ReactRailsUJS.mountComponents(target);
344+
}
345+
ReactRailsUJS.handleUnmount = function(e) {
346+
var target = undefined;
347+
if (e && e.target) {
348+
target = e.target;
349+
}
350+
ReactRailsUJS.unmountComponents(target);
301351
}
302352

353+
303354
if (typeof window !== "undefined") {
304355
// Only setup events for browser (not server-rendering)
305-
detectEvents(ReactRailsUJS)
356+
ReactRailsUJS.detectEvents()
306357
}
307358

308359
// It's a bit of a no-no to populate the global namespace,
@@ -324,12 +375,22 @@ module.exports = {
324375
setup: function(ujs) {
325376
if (ujs.jQuery) {
326377
// Use jQuery if it's present:
327-
ujs.jQuery(function(){ujs.mountComponents()});
378+
ujs.handleEvent("ready",ujs.handleMount);
328379
} else if ('addEventListener' in window) {
329-
document.addEventListener('DOMContentLoaded', function(){ujs.mountComponents()});
380+
ujs.handleEvent('DOMContentLoaded', ujs.handleMount);
330381
} else {
331382
// add support to IE8 without jQuery
332-
window.attachEvent('onload', function() { ujs.mountComponents() });
383+
ujs.handleEvent('onload', ujs.handleMount);
384+
}
385+
},
386+
387+
teardown: function(ujs) {
388+
if (ujs.jQuery) {
389+
ujs.removeEvent("ready", ujs.handleMount);
390+
} else if ('addEventListener' in window) {
391+
ujs.removeEvent('DOMContentLoaded', ujs.handleMount);
392+
} else {
393+
ujs.removeEvent('onload', ujs.handleMount);
333394
}
334395
}
335396
}
@@ -342,10 +403,16 @@ module.exports = {
342403
module.exports = {
343404
// pjax support
344405
setup: function(ujs) {
345-
ujs.handleEvent('ready', function() { ujs.mountComponents() });
346-
ujs.handleEvent('pjax:end', function(e) { ujs.mountComponents(e.target) });
347-
ujs.handleEvent('pjax:beforeReplace', function(e) { ujs.unmountComponents(e.target) });
348-
}
406+
ujs.handleEvent('ready', ujs.handleMount);
407+
ujs.handleEvent('pjax:end', ujs.handleMount);
408+
ujs.handleEvent('pjax:beforeReplace', ujs.handleUnmount);
409+
},
410+
411+
teardown: function() {
412+
ujs.removeEvent('ready', ujs.handleMount);
413+
ujs.removeEvent('pjax:end', ujs.handleMount);
414+
ujs.removeEvent('pjax:beforeReplace', ujs.handleUnmount);
415+
},
349416
}
350417

351418

@@ -356,9 +423,15 @@ module.exports = {
356423
module.exports = {
357424
// Turbolinks 5+ got rid of named events (?!)
358425
setup: function(ujs) {
359-
ujs.handleEvent('DOMContentLoaded', function() { ujs.mountComponents() })
360-
ujs.handleEvent('turbolinks:render', function() { ujs.mountComponents() })
361-
ujs.handleEvent('turbolinks:before-render', function() { ujs.unmountComponents() })
426+
ujs.handleEvent('DOMContentLoaded', ujs.handleMount)
427+
ujs.handleEvent('turbolinks:render', ujs.handleMount)
428+
ujs.handleEvent('turbolinks:before-render', ujs.handleUnmount)
429+
},
430+
431+
teardown: function(ujs) {
432+
ujs.removeEvent('DOMContentLoaded', ujs.handleMount)
433+
ujs.removeEvent('turbolinks:render', ujs.handleMount)
434+
ujs.removeEvent('turbolinks:before-render', ujs.handleUnmount)
362435
},
363436
}
364437

@@ -371,8 +444,12 @@ module.exports = {
371444
// Attach handlers to Turbolinks-Classic events
372445
// for mounting and unmounting components
373446
setup: function(ujs) {
374-
ujs.handleEvent(Turbolinks.EVENTS.CHANGE, function() { ujs.mountComponents() });
375-
ujs.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, function() { ujs.unmountComponents() });
447+
ujs.handleEvent(Turbolinks.EVENTS.CHANGE, ujs.handleMount);
448+
ujs.handleEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, ujs.handleUnmount);
449+
},
450+
teardown: function(ujs) {
451+
ujs.removeEvent(Turbolinks.EVENTS.CHANGE, ujs.handleMount);
452+
ujs.removeEvent(Turbolinks.EVENTS.BEFORE_UNLOAD, ujs.handleUnmount);
376453
}
377454
}
378455

@@ -388,8 +465,12 @@ module.exports = {
388465
// https://github.com/reactjs/react-rails/issues/87
389466
setup: function(ujs) {
390467
Turbolinks.pagesCached(0)
391-
ujs.handleEvent('page:change', function() { ujs.mountComponents() });
392-
ujs.handleEvent('page:receive', function() { ujs.unmountComponents() });
468+
ujs.handleEvent('page:change', ujs.handleMount);
469+
ujs.handleEvent('page:receive', ujs.handleUnmount);
470+
},
471+
teardown: function(ujs) {
472+
ujs.removeEvent('page:change', ujs.handleMount);
473+
ujs.removeEvent('page:receive', ujs.handleUnmount);
393474
}
394475
}
395476

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /