示例#1
0
        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);
        }
示例#2
0
        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);
        }