@mjackson/my-react
Version:
A lightweight, drop-in replacement for React that avoids using ES6 classes and "this"
224 lines (181 loc) • 5.99 kB
JavaScript
import React from 'react';
/**
* Copyright 2013-2015, Facebook, Inc.
* All rights reserved.
*
* This source code is licensed under the BSD-style license found in the
* LICENSE file in the root directory of this source tree. An additional grant
* of patent rights can be found in the PATENTS file in the same directory.
*/
/**
* Use invariant() to assert state which your program assumes to be true.
*
* Provide sprintf-style format (only %s is supported) and arguments
* to provide information about what broke and what you were
* expecting.
*
* The invariant message will be stripped in production, but the invariant
* will remain to ensure logic does not differ in production.
*/
var NODE_ENV = undefined;
var invariant = function(condition, format, a, b, c, d, e, f) {
if (NODE_ENV !== 'production') {
if (format === undefined) {
throw new Error('invariant requires an error message argument');
}
}
if (!condition) {
var error;
if (format === undefined) {
error = new Error(
'Minified exception occurred; use the non-minified dev environment ' +
'for the full error message and additional helpful warnings.'
);
} else {
var args = [a, b, c, d, e, f];
var argIndex = 0;
error = new Error(
format.replace(/%s/g, function() { return args[argIndex++]; })
);
error.name = 'Invariant Violation';
}
error.framesToPop = 1; // we don't care about invariant's own frame
throw error;
}
};
var invariant_1 = invariant;
/**
* React class properties we support.
*/
var ReactStatics = {
displayName: true,
defaultProps: true,
propTypes: true,
contextTypes: true,
childContextTypes: true
/**
* React lifecycle methods we support.
*/
};var ReactLifecycle = {
getChildContext: true,
componentWillMount: true,
componentDidMount: true,
shouldComponentUpdate: true,
componentWillUpdate: true,
componentDidUpdate: true,
componentWillUnmount: true
/**
* Custom lifecycle methods.
*/
};var MyLifecycle = {
setupComponent: true,
getElement: true,
getNextState: true
};
function isFunction(obj) {
return typeof obj === "function";
}
/**
* Creates a new myReact component. The argument is the "component definition"
* object and must contain at least the following property:
*
* - getElement(my)
*
* The component definition may also contain any of the following properties:
*
* - displayName
* - defaultProps
* - propTypes
* - contextTypes
* - childContextTypes
* - getChildContext(my)
* - setupComponent(my)
* - componentWillMount(my)
* - componentDidMount(my)
* - getNextState(my, nextProps)
* - shouldComponentUpdate(my, nextProps, nextState)
* - componentWillUpdate(my, nextProps, nextState)
* - componentDidUpdate(my, prevProps, prevState)
* - componentWillUnmount(my)
*
* Additionally, the component definition may just be a function (instead of an
* object), in which case that function will be used as the `getElement` value.
*/
function createComponent(def) {
invariant_1(def, "createComponent is missing the component definition");
// Support plain functions as well as objects.
var getElement = def.getElement || def;
invariant_1(getElement, "getElement is missing from the component definition");
invariant_1(isFunction(getElement), "getElement must be a function");
var setupComponent = def.setupComponent;
invariant_1(!setupComponent || isFunction(setupComponent), "setupComponent must be a function");
function Component(props) {
React.Component.call(this, props);
// Auto-bind instance methods.
Object.keys(instanceMethods).forEach(function (key) {
this[key] = instanceMethods[key].bind(undefined, this);
}, this);
if (setupComponent) setupComponent(this);
}
var proto = Component.prototype;
Object.setPrototypeOf(proto, React.Component.prototype);
proto.render = function () {
return getElement(this);
};
var getNextState = def.getNextState;
if (getNextState) {
invariant_1(isFunction(getNextState), "getNextState must be a function");
proto.componentWillReceiveProps = function (nextProps) {
var nextState = getNextState(this, nextProps);
if (nextState) this.setState(nextState);
};
}
var instanceMethods = {};
Object.keys(def).forEach(function (key) {
var value = def[key];
if (ReactStatics[key]) {
Component[key] = value;
} else if (ReactLifecycle[key]) {
invariant_1(isFunction(value), 'Lifecycle method "%s" must be a function', key);
// Keep React lifecycle methods on the prototype, for efficiency.
proto[key] = function (a, b) {
return value(this, a, b);
};
} else if (!MyLifecycle[key]) {
invariant_1(isFunction(value), 'Unable to bind property "%s"; it must be a function', key);
// Save this method to be bound directly to the object at creation.
instanceMethods[key] = value;
}
});
// Use the names of functions as the displayName.
if (!Component.displayName && isFunction(def)) {
Component.displayName = def.name;
}
return Component;
}
/**
* A cache of dynamically-created components.
*/
var cache = new Map();
/**
* Creates a new React element using a component definition. New components
* are automatically created from definitions on-the-fly as needed.
*/
function createElement() {
var args = Array.prototype.slice.call(arguments, 0);
var def = args.shift();
invariant_1(def, "createElement needs a component definition");
var component = typeof def === "string" || typeof def === "function" ? def : cache.get(def);
if (component == null) {
component = createComponent(def);
cache.set(def, component);
}
args.unshift(component);
return React.createElement.apply(React, args);
}
var index = {
createComponent: createComponent,
createElement: createElement
};
export { createComponent, createElement };
export default index;