Skip to content

Commit

Permalink
not store instance to global, remove custom I13nNode support
Browse files Browse the repository at this point in the history
  • Loading branch information
kaesonho committed Aug 8, 2016
1 parent ce36e52 commit 65e5a54
Show file tree
Hide file tree
Showing 7 changed files with 66 additions and 83 deletions.
36 changes: 4 additions & 32 deletions src/libs/ReactI13n.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ var debug = debugLib('ReactI13n');
var EventsQueue = require('./EventsQueue');
var I13nNode = require('./I13nNode');
var DEFAULT_HANDLER_TIMEOUT = 1000;
var GLOBAL_OBJECT = ('client' === ENVIRONMENT) ? window : global;
var ENVIRONMENT = (typeof window !== 'undefined') ? 'client' : 'server';

// export the debug lib in client side
Expand All @@ -24,7 +23,6 @@ if ('client' === ENVIRONMENT) {
* @param {Object} options options object
* @param {Boolean} options.isViewportEnabled if enable viewport checking
* @param {Object} options.rootModelData model data of root i13n node
* @param {Object} options.i13nNodeClass the i13nNode class, you can inherit it with your own functionalities
* @param {String} options.scrollableContainerId id of the scrollable element that your components
* reside within. Normally, you won't need to provide a value for this. This is only to
* support viewport checking when your components are contained within a scrollable element.
Expand All @@ -34,40 +32,24 @@ if ('client' === ENVIRONMENT) {
var ReactI13n = function ReactI13n (options) {
debug('init', options);
options = options || {};
this._i13nNodeClass = 'function' === typeof options.i13nNodeClass ? options.i13nNodeClass : I13nNode;

this._plugins = {};
this._eventsQueues = {};
this._isViewportEnabled = options.isViewportEnabled || false;
this._rootModelData = options.rootModelData || {};
this._handlerTimeout = options.handlerTimeout || DEFAULT_HANDLER_TIMEOUT;
this._scrollableContainerId = options.scrollableContainerId || undefined;

// set itself to the global object so that we can get it anywhere by the static function getInstance
GLOBAL_OBJECT.reactI13n = this;
};

/**
* Get ReactI13n Instance
* @method getInstance
* @return the ReactI13n instance
* @static
*/
ReactI13n.getInstance = function getInstance () {
return GLOBAL_OBJECT.reactI13n;
};

/**
* Create root node and set to the global object
* @method createRootI13nNode
*/
ReactI13n.prototype.createRootI13nNode = function createRootI13nNode () {
var I13nNodeClass = this.getI13nNodeClass();
GLOBAL_OBJECT.rootI13nNode = new I13nNodeClass(null, this._rootModelData, false);
this._rootI13nNode = new I13nNode(null, this._rootModelData, false);
if ('client' === ENVIRONMENT) {
GLOBAL_OBJECT.rootI13nNode.setDOMNode(document.body);
this._rootI13nNode.setDOMNode(document.body);
}
return GLOBAL_OBJECT.rootI13nNode;
return this._rootI13nNode;
};

/**
Expand Down Expand Up @@ -146,15 +128,6 @@ ReactI13n.prototype.getEventHandlers = function getEventHandlers (eventName, pay
return promiseHandlers;
};

/**
* Get I13n node class
* @method getI13nNodeClass
* @return {Object} I13nNode class
*/
ReactI13n.prototype.getI13nNodeClass = function getI13nNodeClass () {
return this._i13nNodeClass;
};

/**
* Get isViewportEnabled value
* @method isViewportEnabled
Expand Down Expand Up @@ -191,7 +164,7 @@ ReactI13n.prototype.getScrollableContainerDOMNode = function getScrollableContai
* @return {Object} root react i13n node
*/
ReactI13n.prototype.getRootI13nNode = function getRootI13nNode () {
return GLOBAL_OBJECT.rootI13nNode;
return this._rootI13nNode;
};

/**
Expand All @@ -202,7 +175,6 @@ ReactI13n.prototype.getRootI13nNode = function getRootI13nNode () {
ReactI13n.prototype.updateOptions = function updateOptions (options) {
debug('updated', options);
options = options || {};
this._i13nNodeClass = 'function' === typeof options.i13nNodeClass ? options.i13nNodeClass : this._i13nNodeClass;
this._isViewportEnabled = (undefined !== options.isViewportEnabled) ?
options.isViewportEnabled : this._isViewportEnabled;
this._rootModelData = options.rootModelData ? options.rootModelData : this._rootModelData;
Expand Down
8 changes: 3 additions & 5 deletions src/mixins/I13nMixin.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ var DebugDashboard = require('../utils/DebugDashboard');
var I13nUtils = require('./I13nUtils');
var listen = require('subscribe-ui-event').listen;
var ReactI13n = require('../libs/ReactI13n');
var I13nNode = require('../libs/I13nNode');
var ViewportMixin = require('./viewport/ViewportMixin');
require('setimmediate');
var IS_DEBUG_MODE = isDebugMode();
Expand Down Expand Up @@ -137,9 +138,6 @@ var I13nMixin = {
* @method componentWillMount
*/
componentWillMount: function () {
if (!this._getReactI13n()) {
return;
}
clearTimeout(pageInitViewportDetectionTimeout);
this._createI13nNode();
this._i13nNode.setReactComponent(this);
Expand Down Expand Up @@ -366,14 +364,14 @@ var I13nMixin = {
_createI13nNode: function () {
// check if reactI13n is initialized successfully, otherwise return
var self = this;
var I13nNode = self._getReactI13n().getI13nNodeClass();
var parentI13nNode = self._getParentI13nNode();
var reactI13n = self._getReactI13n();
// TODO @kaesonho remove BC for model
self._i13nNode = new I13nNode(
parentI13nNode,
self.props.i13nModel || self.props.model,
self.isLeafNode(),
self._getReactI13n().isViewportEnabled());
reactI13n && reactI13n.isViewportEnabled());
},

/**
Expand Down
31 changes: 20 additions & 11 deletions src/mixins/I13nUtils.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
var React = require('react');
var ReactI13n = require('../libs/ReactI13n');
var IS_CLIENT = typeof window !== 'undefined';

/**
* React.js I13n Utils Mixin
Expand All @@ -12,29 +13,33 @@ var I13nUtils = {
i13n: React.PropTypes.shape({
executeEvent: React.PropTypes.func,
getI13nNode: React.PropTypes.func,
parentI13nNode: React.PropTypes.object
parentI13nNode: React.PropTypes.object,
reactI13nInstance: React.PropTypes.object
})
},

childContextTypes: {
i13n: React.PropTypes.shape({
executeEvent: React.PropTypes.func,
getI13nNode: React.PropTypes.func,
parentI13nNode: React.PropTypes.object
parentI13nNode: React.PropTypes.object,
reactI13nInstance: React.PropTypes.object
})
},


/**
* getChildContext
* @method getChildContext
*/
getChildContext: function () {
return {
i13n: {
i13n: Object.assign({}, this.context.i13n, {
executeEvent: this.executeI13nEvent,
getI13nNode: this.getI13nNode,
parentI13nNode: this._i13nNode
}
parentI13nNode: this._i13nNode,
reactI13nInstance: this._getReactI13n()
})
};
},

Expand Down Expand Up @@ -63,9 +68,6 @@ var I13nUtils = {
* @return {Object} i13n node
*/
getI13nNode: function () {
if (!this._getReactI13n()) {
return;
}
return this._i13nNode || this._getParentI13nNode();
},

Expand All @@ -76,7 +78,13 @@ var I13nUtils = {
* @return {Object} react i13n instance
*/
_getReactI13n: function () {
return ReactI13n.getInstance();
var globalReactI13n;
if (IS_CLIENT) {
globalReactI13n = window._reactI13nInstance;
}
return this._reactI13nInstance ||
(this.context && this.context.i13n && this.context.i13n.reactI13nInstance) ||
globalReactI13n;
},

/**
Expand All @@ -86,9 +94,10 @@ var I13nUtils = {
* @return {Object} parent i13n node
*/
_getParentI13nNode: function () {
// https://twitter.com/andreypopp/status/578974316483608576, get the context from parent context
var reactI13n = this._getReactI13n();
var context = this.context;
return (context && context.i13n && context.i13n.parentI13nNode) || this._getReactI13n().getRootI13nNode();
return (context && context.i13n && context.i13n.parentI13nNode) ||
(reactI13n && reactI13n.getRootI13nNode());
}
}

Expand Down
22 changes: 14 additions & 8 deletions src/utils/setupI13n.js
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
var React = require('react');
var ReactI13n = require('../libs/ReactI13n');
var I13nUtils = require('../mixins/I13nUtils');
var IS_CLIENT = typeof window !== 'undefined';

/**
* Create an app level component with i13n setup
Expand All @@ -25,16 +26,11 @@ module.exports = function setupI13n (Component, options, plugins) {
var RootI13nComponent;
var componentName = Component.displayName || Component.name;

var reactI13n = new ReactI13n(options);
plugins.forEach(function setPlugin(plugin) {
reactI13n.plug(plugin);
});

RootI13nComponent = React.createClass({

mixins: [I13nUtils],

displayName: options.displayName || ('RootI13n' + componentName),
displayName: options.displayName || ('setupI13n(' + componentName + ')'),

autobind: false,

Expand All @@ -43,15 +39,25 @@ module.exports = function setupI13n (Component, options, plugins) {
* @method componentWillMount
*/
componentWillMount: function () {
var reactI13n = ReactI13n.getInstance();
var reactI13n = new ReactI13n(options);
this._reactI13nInstance = reactI13n;
// we might have case to access reactI13n instance to execute event outside react components
// assign reactI13n to window
if (IS_CLIENT) {
window._reactI13nInstance = reactI13n;
}
plugins.forEach(function setPlugin(plugin) {
reactI13n.plug(plugin);
});
reactI13n.createRootI13nNode();
},

render: function () {
var props = Object.assign({}, {
i13n: {
executeEvent: this.executeI13nEvent,
getI13nNode: this.getI13nNode
getI13nNode: this.getI13nNode,
reactI13nInstance: this._reactI13nInstance
}
}, this.props);
return React.createElement(
Expand Down
1 change: 0 additions & 1 deletion tests/unit/libs/ReactI13n.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ describe('ReactI13n', function () {
var reactI13n = new ReactI13n({
isViewportEnabled: true
});
expect(ReactI13n.getInstance()).to.eql(reactI13n); // static function should be able to get the instance we created
expect(reactI13n.isViewportEnabled()).to.eql(true);
});

Expand Down
49 changes: 24 additions & 25 deletions tests/unit/utils/createI13nNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,16 +40,14 @@ var mockSubscribe = {
}
};
var mockClickHandler = function () {};
MockReactI13n.getInstance = function () {
return mockData.reactI13n;
};

function findProps(elem) {
try {
return elem[Object.keys(elem).find(function (key) {
return key.indexOf('__reactInternalInstance') === 0;
})]._currentElement.props;
} catch (e) {}
try {
return elem[Object.keys(elem).find(function (key) {
return key.indexOf('__reactInternalInstance') === 0 ||
key.indexOf('_reactInternalComponent') === 0;
})]._currentElement.props;
} catch (e) {}
}

describe('createI13nNode', function () {
Expand Down Expand Up @@ -89,6 +87,7 @@ describe('createI13nNode', function () {
return mockData.isViewportEnabled;
}
};
global.window._reactI13nInstance = mockData.reactI13n;

done();
});
Expand Down Expand Up @@ -182,7 +181,7 @@ describe('createI13nNode', function () {
}
});
var I13nTestComponent = createI13nNode(TestComponent);
mockData.reactI13n = null;
window._reactI13nInstance = null;
var container = document.createElement('div');
var component = ReactDOM.render(React.createElement(I13nTestComponent, {}), container);
expect(component).to.be.an('object');
Expand Down Expand Up @@ -442,21 +441,21 @@ describe('createI13nNode', function () {
});

it('should not pass i13n props to string components', function () {
var props = {
i13nModel: {sec: 'foo'},
href: '#/foobar'
};
var I13nTestComponent = createI13nNode('a', {
follow: true,
isLeafNode: true,
bindClickEvent: true,
scanLinks: {enable: true}
}, {
refToWrappedComponent: 'wrappedElement'
});
mockData.reactI13n.execute = function () {};
var container = document.createElement('div');
var component = ReactDOM.render(React.createElement(I13nTestComponent, props), container);
expect(findProps(component.refs.wrappedElement)).to.eql({href: '#/foobar', children: undefined});
var props = {
i13nModel: {sec: 'foo'},
href: '#/foobar'
};
var I13nTestComponent = createI13nNode('a', {
follow: true,
isLeafNode: true,
bindClickEvent: true,
scanLinks: {enable: true}
}, {
refToWrappedComponent: 'wrappedElement'
});
mockData.reactI13n.execute = function () {};
var container = document.createElement('div');
var component = ReactDOM.render(React.createElement(I13nTestComponent, props), container);
expect(findProps(component.refs.wrappedElement)).to.eql({href: '#/foobar', children: undefined});
});
});
2 changes: 1 addition & 1 deletion tests/unit/utils/setupI13n.js
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,7 @@ describe('setupI13n', function () {

// check the initial state is correct after render
var I13nTestApp = setupI13n(TestApp, mockData.options, [mockData.plugin]);
expect(I13nTestApp.displayName).to.eql('RootI13nTestApp');
expect(I13nTestApp.displayName).to.eql('setupI13n(TestApp)');
var container = document.createElement('div');
var component = ReactDOM.render(React.createElement(I13nTestApp, {}), container);
expect(mockData.reactI13n._options).to.eql(mockData.options);
Expand Down

0 comments on commit 65e5a54

Please sign in to comment.