I should preface this by saying I understand very little about es7 decorators. Basically what I want is a decorator called @model
that adds a function to a component called model
. So for example I'd call it like
@model class FooBar extends Component { }
and then the FooBar class would now have the model function.
Here's what I tried:
Model.js
export default function reactModelFactory( ctx ){
return (key)=>{
return {
onChange: (e)=>ctx.setState({[key]:e.target.value}),
value: ctx.state[key],
name: key
};
};
};
function modelDecorator() {
return function(ctx){
return class extends ctx{
constructor(...args){
super(...args);
this.model = reactModelFactory(this);
}
}
}
}
export { modelDecorator as model };
Login.js
import React,{PureComponent} from 'react';
import {model} from './Model';
@model class Login extends PureComponent{}
React throws with error message:
TypeError: Super expression must either be null or a function, not object
I have no idea what this means. I'm looking for some help in getting my decorator working, and a bonus would be to understand the concept of decorators at all.
To add to @dfsq's answer (I'm assuming it does what you want), you can go a step further in terms of interface performance by adding model()
to the prototype
instead of to each instance like this:
export default function reactModelFactory() {
return function model (key) {
return {
onChange: (e) => this.setState({ [key]: e.target.value }),
value: this.state[key],
name: key
};
};
};
function modelDecorator(Class) {
Object.defineProperty(Class.prototype, 'model', {
value: reactModelFactory(),
configurable: true,
writable: true
});
return Class;
}
This is much better for performance as it causes the decorator to modify the existing class's prototype
a single time with the model
member method, rather than attaching a scoped copy of the model
method within the anonymous extended class's constructor
each time a new instance is constructed.
To clarify, this means that in @dfsq's answer, reactModelFactory()
is invoked each time a new instance is constructed, while in this answer, reactModelFactory()
is only invoked a single time when the decorator is activated on the class.
The reason I used configurable
and writable
in the property descriptor is because that's how the class { }
syntax natively defines member methods on the prototype
:
class Dummy {
dummy () {}
}
let {
configurable,
writable,
enumerable
} = Object.getOwnPropertyDescriptor(Dummy.prototype, 'dummy');
console.log('configurable', configurable);
console.log('enumerable', enumerable);
console.log('writable', writable);