8

ES7 introduces the concept of static property and method definitions. Along with an ES7-capable transpiler, these can be used in React to specify validators and defaults for props, like so:

export default class ComponentOne extends React.Component {
    static propTypes = {
        foo: React.PropTypes.string
    }
    static defaultProps = {
        foo: 'bar'
    }
    // ...
}

This is super handy, but gets tricky when subclasses come into play. For example, say the following module is added to the same codebase as ComponentOne above:

export default class ComponentTwo extends ComponentOne {
    static propTypes = {
        baz: React.PropTypes.number
    }
    static defaultProps = {
        baz: 42
    }
    // ...
}

I'd like ComponentTwo to "inherit" the property validators and defaults of its superclass, ComponentOne. Instead, propTypes and defaultProps on ComponentTwo shadow those on ComponentOne, and React tosses out those defined on ComponentOne.

Since super is a reference to the current class's prototype, and static is supposed to reference values hung directly off the prototype, I thought this might work:

import _ from 'lodash';
export default class ComponentTwo extends ComponentOne {
    static propTypes = _.merge(super.propTypes, {
        baz: React.PropTypes.number
    });
}

However, this generates an error, presumably from Babel: Parsing error: 'super' outside of function or class.

This works, but is not very portable:

export default class ComponentTwo extends ComponentOne {
    static propTypes = Object.assign({
        baz: React.PropTypes.number
    }, ComponentOne.propTypes);
}

Are there any other ways to do this more cleanly/reusably?

ericsoco
  • 24,913
  • 29
  • 97
  • 127
  • Please be aware of the caveats when inheriting classes with static properties: "If you're inheriting from a class then static properties are inherited from it via `__proto__`, this is widely supported but you may run into problems with much older browsers. NOTE: `__proto__` is not supported on IE <= 10 so static properties will not be inherited." source: https://babeljs.io/docs/advanced/caveats/ – Raspo Oct 28 '15 at 13:40
  • Have you tried doing the `super.propTypes` merge in the constructor? –  Nov 03 '15 at 12:32
  • @Mat I haven't, but given that `props` come into the constructor with defaults already set, I imagine that would be too late. – ericsoco Nov 03 '15 at 19:19

2 Answers2

1

I stumbled upon this question, and it's been almost 3 years, but who know, someone might need it. (And it's still relevant)

Given that when you extend a class it automatically inherits of its parent class, you would not need to overwrite the static propTypes property.

Given a parent class:

class Parent {
  static propTypes = {
    parentProp: PropTypes.string
  }
}

If you don't want to add other propTypes/defaultProps, you can simply:

class Children extends Parent {
  // Do not declare the propTypes, it will extends by itself.
}
console.log(Children.propTypes); // Will output an object with parentProp in it

If you want to explicitly tell that you extends Parent propTypes, or add new propTypes:

class Children extends Parent {
  static propTypes = {
    ...Parent.propTypes, // Yes, you can spread static properties like everything else
    childProp: Proptypes.number,
  }
}

Small note, for this to work properly with Babel, you might need to include the transform-es2015-classes babel plugin in your plugins or preset. My .babelrc:

"presets": [
  ["env", {
    "include": ["transform-es2015-classes"]
  }],
  "stage-0",
  "react"
],

Hope this helps!

ChrisR
  • 3,922
  • 1
  • 14
  • 24
0

Curiously enough, using super works for static methods. I'd think it should work for static properties too. To me, then, it feels more natural to use the super class name directly:

export default class ComponentTwo extends ComponentOne {
  static propTypes = _.merge({}, ComponentOne.propTypes, {
    baz: React.PropTypes.number
  });
}

But, to use super, one workaround I can think of is using a static method to initialize the property, which unfortunately would have to be called manually:

class ComponentTwo extends ComponentOne {
  static _init() { 
    this.propTypes = _.merge({}, super.propTypes, {
      baz: React.PropTypes.number
    });
  }
}
ComponentTwo._init();
Jordão
  • 55,340
  • 13
  • 112
  • 144
  • Isn't this just a refactor of the suggestion at the end of my post? Using `lodash` instead of `Object.assign`? My problem with that is explicitly referencing `ComponentOne` instead of being able to refer to the "super" class. Not a big deal since you have to reference it in the `extends` statement, but not ideal. – ericsoco Dec 14 '15 at 18:32
  • Yes, that's precisely that, but with the right way of using _merge_. – Jordão Dec 15 '15 at 02:12
  • Also added another alternative, which I think is worse. – Jordão Dec 15 '15 at 03:30
  • U can probably get rid of the ini function by using a static get propTypes() – joshua.thomas.bird Jun 04 '21 at 05:11