Skip to content

Commit

Permalink
(WIP) Get rid of _source
Browse files Browse the repository at this point in the history
  • Loading branch information
gaearon committed May 1, 2016
1 parent 146f8d5 commit 90ea224
Show file tree
Hide file tree
Showing 3 changed files with 17 additions and 81 deletions.
16 changes: 2 additions & 14 deletions src/babel/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,20 +9,8 @@ const buildTagger = template(`
return;
}
if (fn.hasOwnProperty("__source")) {
return;
}
try {
Object.defineProperty(fn, "__source", {
enumerable: false,
configurable: true,
value: {
fileName: FILENAME,
localName: localName
}
});
} catch (err) {}
var id = FILENAME + '#' + localName;
__REACT_HOT_LOADER__.set(id, fn);
}
REGISTRATIONS
})();
Expand Down
20 changes: 2 additions & 18 deletions src/patch.dev.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,34 +2,18 @@

const React = require('react');
const resolveType = require('./resolveType');
const { isReactRouterish, extractRouteHandlerComponents } = require('./fixupReactRouter');

if (React.createElement.isPatchedByReactHotLoader) {
throw new Error('Cannot patch React twice.');
}

const createElement = React.createElement;
function patchedCreateElement(type, props, ...args) {
function patchedCreateElement(type, ...args) {
// Trick React into rendering a proxy so that
// its state is preserved when the class changes.
// This will update the proxy if it's for a known type.
const resolvedType = resolveType(type);
const element = createElement(resolvedType, props, ...args);

// This is lame but let's focus on shipping.
// https://github.com/gaearon/react-hot-loader/issues/249
if (isReactRouterish(type)) {
// Ideally we want to teach React Router to receive children.
// We're not in a perfect world, and a dirty workaround works for now.
// https://github.com/reactjs/react-router/issues/2182
const resolvedProps = element.props;
const routeHandlers = extractRouteHandlerComponents(resolvedProps, resolveType);
// Side effect 😱
// Force proxies to update since React Router ignores new props.
routeHandlers.forEach(resolveType);
}

return element;
return createElement(resolvedType, ...args);
}
patchedCreateElement.isPatchedByReactHotLoader = true;
React.createElement = patchedCreateElement;
62 changes: 13 additions & 49 deletions src/resolveType.js
Original file line number Diff line number Diff line change
@@ -1,72 +1,36 @@
const createProxy = require('react-proxy').default;

let proxies = {};
let warnedAboutTypes = {};
let proxiesByID = {};
let idsByType = new WeakMap();

function setFlag(obj, key) {
try {
Object.defineProperty(obj, key, {
configurable: true,
enumerable: false,
value: true
});
} catch (err) {}
}
window.__REACT_HOT_LOADER__ = {
set(id, component) {
idsByType.set(component, id);
}
};

function resolveType(type) {
// We only care about composite components
if (!type || typeof type === 'string') {
return type;
}

// If the type is not tagged, return it as is.
if (
!Object.hasOwnProperty.call(type, '__source') ||
!type.__source ||
!type.__source.fileName ||
!type.__source.localName
) {
setFlag(type, '__noSourceFound');
return type;
}

// Uniquely identifiy the component in code across reloads.
const source = type.__source;
const id = source.fileName + '#' + source.localName;

if (type.hasOwnProperty('__noSourceFound')) {
// This component didn't have a source last time, but now it has?
// This means createElement() was called during module definition.
// Bail out, or the component will be unmounted unexpectedly this time,
// as we'll return proxy but we returned the original class the last time.
// https://github.com/gaearon/react-hot-loader/issues/241
if (!warnedAboutTypes[id]) {
warnedAboutTypes[id] = true;
console.error(
`React Hot Loader: ${source.localName} from ${source.fileName} will not ` +
`hot reload correctly because it contains an imperative call like ` +
`ReactDOM.render() in the same file. Split ${source.localName} into a ` +
`separate file for hot reloading to work.`
);
}
var id = idsByType.get(type);
if (!id) {
return type;
}

// We use React Proxy to generate classes that behave almost
// the same way as the original classes but are updatable with
// new versions without destroying original instances.
if (!proxies[id]) {
proxies[id] = createProxy(type);
if (!proxiesByID[id]) {
proxiesByID[id] = createProxy(type);
} else if (!type.hasOwnProperty('__hasBeenUsedForProxy')) {
proxies[id].update(type);
proxiesByID[id].update(type);
}
// Don't update proxy with the same class.
// This makes sure stale old classes never revive.
// https://github.com/gaearon/react-hot-loader/issues/248
setFlag(type, '__hasBeenUsedForProxy');

// Give proxy class to React instead of the real class.
return proxies[id].get();
return proxiesByID[id].get();
}

module.exports = resolveType;

0 comments on commit 90ea224

Please sign in to comment.