private Func <TProps, ReactElement> CreateStatelessRenderFunction() { // We need to prepare a function to give to React.createElement that takes a props reference and maintains that for the instance of the element for the // duration of the Render call AND for any work that might happen later, such as in an OnChange callback (or other event-handler). To do this, we need an // instance that will capture this props value and that has all of the functionality of the original component (such as any functions that it has). The // best way that I can think of is to use Object.create to prepare a new instance, taking the prototype of the component class, and then setting its // props reference, then wrapping this all in a function that calls its Render function, binding to this instance. This woud mean that the constructor // would not get called on the component, but that's just the same as for stateful components (from the Component class). /*@ * var classPrototype = this.constructor.prototype; * var scopeBoundFunction = function(props) { * var target = Object.create(classPrototype); * target.props = props; * return target.render.apply(target, []); * } */ // We have an anonymous function for the renderer now but it would better to name it, since React Dev Tools will use show the function name (if defined) as // the component name in the tree. The only way to do this is, unfortunately, with eval - but the only dynamic content is the class name (which should be // safe to use since valid C# class names should be valid JavaScript function names, with no escaping required) and this work is only performed once per // class, since it is stored in a static variable - so the eval calls will be made very infrequently (so performance is not a concern). var className = ComponentNameHelpers.GetDisplayName(this); Func <TProps, ReactElement> namedScopeBoundFunction = null; /*@ * eval("namedScopeBoundFunction = function " + className + "(props) { return scopeBoundFunction(props); };"); */ return(namedScopeBoundFunction); }
public static object CreateClass(object template, Type baseComponent) { if (template == null) { throw new ArgumentNullException(nameof(template)); } if (baseComponent == null) { throw new ArgumentNullException(nameof(baseComponent)); } var displayName = ComponentNameHelpers.GetDisplayName(template); object reactComponentClass = null; /*@ * function getOwnPropertyDescriptors(obj) { // IE11 doesn't support Object.getOwnPropertyDescriptors so use this * var result = { }; * var arrPropertyNames = Object.getOwnPropertyNames(obj); // IE11 doesn't support "var key of Reflect.ownKeys(obj)" but this approach should suffice for Bridge classes * for (var i = 0; i < arrPropertyNames.length; i++) { * var key = arrPropertyNames[i]; * result[key] = Object.getOwnPropertyDescriptor(obj, key); * } * return result; * } * * // Use the displayName to name the component class function (React DevTools will use this) * eval("reactComponentClass = function " + displayName + "(props) { Bridge.React.ReactComponentClassCreator.initialiseComponentState(this, props); }"); * * // Set the React.Component base class * reactComponentClass.prototype = Object.create( * React.Component && React.Component.prototype, * { constructor: { value: reactComponentClass, enumerable: false, writable: true, configurable: true } } * ); * if (Object.setPrototypeOf) { * Object.setPrototypeOf(reactComponentClass, React.Component); * } * else { * reactComponentClass.__proto__ = React.Component; * } * * // Attach the members * // - Get all class prototypes until hit the component base class (there's no need to go down to System.Object) * // - Apply the members in reverse order (in case any members are named on a derived class and a base class, the derived class should "win" - this won't break calling * // methods on the base due to the way that Bridge generates that code) * var protoStack = []; * var o = template.__proto__; * while (o) { * protoStack.push(o); * if ((o.$$fullname || "") === baseComponent.$$fullname) { * break; * } * o = o.__proto__; * } * for (var i = protoStack.length - 1; i >= 0; i--) { * o = protoStack[i]; * var descriptors = getOwnPropertyDescriptors(o); * for (var name in descriptors) { * var descriptor = descriptors[name]; * Object.defineProperty(reactComponentClass.prototype, name, descriptor); * } * } */ return(reactComponentClass); }