-1

I am using a parent class in my app to provide some basic functionality to its children. It looks roughly like this:

class Base {
  constructor(stream) {
    stream.subscribe(this.onData)
  }

  onData(data) {
    throw new Error('"onData" method must be implemented')
  }
}

class Child extends Base {
  onData(data) {
    // do stuff...
  }
}

That works fine and when I instantiate the Child, Base passes Child.onData to the stream The only problem is scope. In Child.onData I make a heavy use of other methods defined in child via this keyword. So when I pass this function as a callback to the stream, everything breaks. The evident solution is this:

class Base {
 constructor(stream) {
   stream.subscribe(this.onData)
 }

 onData = (data) => {
   throw new Error('"onData" method must be implemented')
 }
}

class Child extends Base {
 onData = (data) => {
   // do stuff...
 }
}

That does solve problems with scope, but now the function that is being passed to the stream is always Base.onData which throws errors. Generally, I could do something like passing the Child.onData to Base constructor. That would work, but what I would like to find is a more elegant solution to this, if it exists

Alex Chashin
  • 3,129
  • 1
  • 13
  • 35
  • I am having a hard time following what your issue is... – Get Off My Lawn Jul 19 '19 at 16:41
  • It is most likely breaking because you need to bind it when you pass it. – Get Off My Lawn Jul 19 '19 at 16:43
  • @GetOffMyLawn basically overridden property is not accessible from parent, and I need it to be – Alex Chashin Jul 19 '19 at 16:43
  • @GetOffMyLawn and I can't bind it before passing, because I can't do anything with `this` before I call the parent constructor – Alex Chashin Jul 19 '19 at 16:43
  • can you add an example of how you are passing/accessing it – Get Off My Lawn Jul 19 '19 at 16:43
  • 1
    That's because the child assigns the `this.onData` property *after* the `super()` constructor ran and installed the old, un-overridden value as the stream handler. So a) don't use an arrow function when you plan to override inherited methods b) [don't call (or otherwise use) overridable methods in the constructor](https://stackoverflow.com/q/3404301/1048572). – Bergi Jul 19 '19 at 16:44
  • 1
    You should simply `bind` the method (or use an arrow function wrapper) inside the `Base` constructor – Bergi Jul 19 '19 at 16:46
  • @Bergi, Ok, I've got the reason of this issue. But I don't pretty much understand, how you suggest to bind it inside `Base` constructor. Could you provide a code snippet please? – Alex Chashin Jul 19 '19 at 16:54
  • @Bergi, ok, I've got it, thank you – Alex Chashin Jul 19 '19 at 17:04

1 Answers1

0

That's why arrow functions in class properties are not that great. If you translate it to a normal ES6 class, this is what happens:

class Child extends Base {
  constructor(...args) {
    super(...args);
    this.onData = (data) => {
      // do stuff...
    };
  }
}

It's rather evident now why using the property inside the Base constructor doesn't work.

Instead, you should use normal method definitions and handle the context problem by binding the method inside the parent constructor:

class Base {
  constructor(stream) {
    if (this.onData == Base.prototype.onData)
      throw new Error('"onData" method must be overridden')
    this.onData = this.onData.bind(this);
    stream.subscribe(this.onData)
  }

  onData(data) {}
}

class Child extends Base {
  onData(data) {
    // do stuff...
  }
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375