Skip to content

ES7 property initializers disappear when combined with certain decorator functions #23

@namuol

Description

@namuol

(Note: I encountered this while using react-transform-webpack-hmr, but I believe the issue to be internal to createProxy.)

When using a @decorator function that copies the original component-class into a new one, static properties specified using ES7 initializers are unaccounted for.

For example, here's a slightly modified version of App.js from react-transform-boilerplate:

import React, { Component } from 'react';

// This is TypeScript's implementation of Extend:
var __extends = function (d, b) {
  for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p];
  function __() { this.constructor = d; }
  __.prototype = b.prototype;
  d.prototype = new __();
};

function decorate (BaseComponent) {
  function DecoratedComponent (...args) {
    console.log("Running decorated constructor!");
    BaseComponent.apply(this, ...args)
  }

  __extends(DecoratedComponent, BaseComponent);

  return DecoratedComponent;
}

@decorate
class Number extends Component {
  static defaultProps = {
    value: 1,
  };

  render() {
    return <h1>{this.props.value} (defaultProps={'' + Number.defaultProps})</h1>;
  }
}

export class App extends Component {
  render() {
    return (
      <div>
        <Number />
        <Number value={42} />
      </div>
    );
  }
}

We expect the rendered output to look like this:

But instead we see this:

For some reason, Counter.defaultProps is not transferred in TypeScript's implementation of __extends when it is defined with ES7 generators.

It may have something to do with how __extends uses hasOwnProperty, but I'm not familiar enough with how react-proxy works under the hood to really take a stab at what might be going on, here.

  • If I remove the @decorator, it works as expected.
  • If I change @decorator to use ES6's extends, (i.e. class DerivedComponent extends Component ...), it works as expected.
  • If I specify defaultProps after the decorator runs (i.e. Counter.defaultProps = ...), it works as expected.

I first encountered this problem when using react-free-style, which is written in TypeScript and provides a decorator function that extends components.

The natural workaround is to avoid static ES7 property initializers, but many of us are already using them, as encouraged by React.

I forked react-transform-boilerplate to make it easier to reproduce the problem:

git clone https://github.com/namuol/react-transform-boilerplate-es7-initializer-bug
cd react-transform-boilerplate-es7-initializer-bug
npm install
npm start
open http://localhost:3000

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions