Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #117 from robrobbins/ujs-api
Expose the React mounting and un-mounting mechanisms
- Loading branch information
Showing
2 changed files
with
90 additions
and
47 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,69 +1,89 @@ | ||
// Unobtrusive scripting adapter for React | ||
(function(document, window, React) { | ||
var CLASS_NAME_ATTR = 'data-react-class'; | ||
var PROPS_ATTR = 'data-react-props'; | ||
/*globals React, Turbolinks*/ | ||
|
||
// Unobtrusive scripting adapter for React | ||
(function(document, window) { | ||
// jQuery is optional. Use it to support legacy browsers. | ||
var $ = (typeof jQuery !== 'undefined') && jQuery; | ||
|
||
var findReactDOMNodes = function() { | ||
var SELECTOR = '[' + CLASS_NAME_ATTR + ']'; | ||
if ($) { | ||
return $(SELECTOR); | ||
} else { | ||
return document.querySelectorAll(SELECTOR); | ||
} | ||
}; | ||
|
||
var mountReactComponents = function() { | ||
var nodes = findReactDOMNodes(); | ||
for (var i = 0; i < nodes.length; ++i) { | ||
var node = nodes[i]; | ||
var className = node.getAttribute(CLASS_NAME_ATTR); | ||
// Assume className is simple and can be found at top-level (window). | ||
// Fallback to eval to handle cases like 'My.React.ComponentName'. | ||
var constructor = window[className] || eval.call(window, className); | ||
var propsJson = node.getAttribute(PROPS_ATTR); | ||
var props = propsJson && JSON.parse(propsJson); | ||
React.render(React.createElement(constructor, props), node); | ||
} | ||
}; | ||
|
||
var unmountReactComponents = function() { | ||
var nodes = findReactDOMNodes(); | ||
for (var i = 0; i < nodes.length; ++i) { | ||
React.unmountComponentAtNode(nodes[i]); | ||
var $ = (typeof window.jQuery !== 'undefined') && window.jQuery; | ||
|
||
// create the namespace | ||
window.ReactRailsUJS = { | ||
CLASS_NAME_ATTR: 'data-react-class', | ||
PROPS_ATTR: 'data-react-props', | ||
// helper method for the mount and unmount methods to find the | ||
// `data-react-class` DOM elements | ||
findDOMNodes: function() { | ||
// we will use fully qualified paths as we do not bind the callbacks | ||
var selector = '[' + window.ReactRailsUJS.CLASS_NAME_ATTR + ']'; | ||
|
||
if ($) { | ||
return $(selector); | ||
} else { | ||
return document.querySelectorAll(selector); | ||
} | ||
}, | ||
|
||
mountComponents: function() { | ||
var nodes = window.ReactRailsUJS.findDOMNodes(); | ||
|
||
for (var i = 0; i < nodes.length; ++i) { | ||
var node = nodes[i]; | ||
var className = node.getAttribute(window.ReactRailsUJS.CLASS_NAME_ATTR); | ||
|
||
// Assume className is simple and can be found at top-level (window). | ||
// Fallback to eval to handle cases like 'My.React.ComponentName'. | ||
var constructor = window[className] || eval.call(window, className); | ||
var propsJson = node.getAttribute(window.ReactRailsUJS.PROPS_ATTR); | ||
var props = propsJson && JSON.parse(propsJson); | ||
|
||
React.render(React.createElement(constructor, props), node); | ||
} | ||
}, | ||
|
||
unmountComponents: function() { | ||
var nodes = window.ReactRailsUJS.findDOMNodes(); | ||
|
||
for (var i = 0; i < nodes.length; ++i) { | ||
var node = nodes[i]; | ||
|
||
React.unmountComponentAtNode(node); | ||
// now remove the `data-react-class` wrapper as well | ||
node.parentElement && node.parentElement.removeChild(node); | ||
} | ||
} | ||
}; | ||
|
||
var handleTurbolinksEvents = function() { | ||
// functions not exposed publicly | ||
function handleTurbolinksEvents () { | ||
var handleEvent; | ||
|
||
if ($) { | ||
handleEvent = function(eventName, callback) { | ||
$(document).on(eventName, callback); | ||
} | ||
}; | ||
|
||
} else { | ||
handleEvent = function(eventName, callback) { | ||
document.addEventListener(eventName, callback); | ||
} | ||
}; | ||
} | ||
handleEvent('page:change', mountReactComponents); | ||
handleEvent('page:receive', unmountReactComponents); | ||
}; | ||
handleEvent('page:change', window.ReactRailsUJS.mountComponents); | ||
handleEvent('page:receive', window.ReactRailsUJS.unmountComponents); | ||
} | ||
|
||
var handleNativeEvents = function() { | ||
if ($) { | ||
$(mountReactComponents); | ||
$(window).unload(unmountReactComponents); | ||
function handleNativeEvents() { | ||
if ($) { | ||
$(window.ReactRailsUJS.mountComponents); | ||
$(window).unload(window.ReactRailsUJS.unmountComponents); | ||
|
||
} else { | ||
document.addEventListener('DOMContentLoaded', mountReactComponents); | ||
window.addEventListener('unload', unmountReactComponents); | ||
document.addEventListener('DOMContentLoaded', window.ReactRailsUJS.mountComponents); | ||
window.addEventListener('unload', window.ReactRailsUJS.unmountComponents); | ||
} | ||
}; | ||
} | ||
|
||
if (typeof Turbolinks !== 'undefined' && Turbolinks.supported) { | ||
handleTurbolinksEvents(); | ||
} else { | ||
handleNativeEvents(); | ||
} | ||
})(document, window, React); | ||
})(document, window); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters