Oleksiï Nikonov
Oleksiï Nikonov

Reputation: 5598

react super, Cannot read property 'call' of undefined

i try to use and overwrite a parent method in child one but get Uncaught TypeError: Cannot read property 'call' of undefined

enter image description here

class BreadcrumbsHome extends React.Component {
  getBreadcrumbs = () => [{name: 'home', url: '/'}];

  render() {
    return <nav>
      <ol className="breadcrumb float-sm-right">
        {!!this.getBreadcrumbs && this.getBreadcrumbs().map((e, i, arr) =>
          <li className={`breadcrumb-item ${i === arr.length - 1 ? 'active' : ''}`}><a
            href={APP_CONTEXT + e.url}>{e.name}</a>/</li>
        )}
      </ol>
    </nav>
  }
}

class BreadcrumbsTypes extends BreadcrumbsHome {

  getBreadcrumbs = () => [
    ...super.getBreadcrumbs(),
    ...this.props.path
  ]

as i read about this problem. Seems to be tied with babel so i tried adding the //noprotect line

I would like to handle with ES6 and avoid such way ParentClass.prototype.myMethod.call(this, arg1, arg2, ..)

Any ideas to fix that?

Upvotes: 3

Views: 1911

Answers (2)

Kun Luo
Kun Luo

Reputation: 153

In Short

Do not use arrow function

class Child extends Parent {

+  getBreadcrumbs(){}
-  getBreadcrumbs = () => {}
}

Upvotes: 1

Patrick Roberts
Patrick Roberts

Reputation: 51946

If you want to maintain the typical virtual polymorphism expected in JavaScript prototypal inheritance, you can't use field initializer syntax, you must use the ES2015 class method syntax:

class BreadcrumbsHome extends React.Component {
  getBreadcrumbs () {
    return [{ name: 'home', url: '/' }];
  }

  render() {
    return <nav>
      <ol className="breadcrumb float-sm-right">
        {this.getBreadcrumbs().map((e, i, { length }) => (
          <li
            key={`${e.name}-${e.url}`}
            className={`breadcrumb-item ${i === length - 1 ? 'active' : ''}`}
          >
            <a href={APP_CONTEXT + e.url}>{e.name}</a> /
          </li>
        ))}
      </ol>
    </nav>
  }
}

class BreadcrumbsTypes extends BreadcrumbsHome {

  getBreadcrumbs () {
    return [
      ...super.getBreadcrumbs(),
      ...this.props.path
    ];
  }

  ...
}

The problem is that field initializers create own properties on each class instance rather than binding to the prototype chain like class methods do, so all you were doing was overwriting the own property getBreadcrumbs initialized by the base class with the own property initialized by the extended class.

You can confirm this using a debugger console:

enter image description here vs. enter image description here

P.S. don't forget to include a unique key property on mapped JSX components :)

I'd also suggest using CSS to style .breadcrumb-item:last-child rather than .breadcrumb-item.active, so your map() callback can simplify the className to

className="breadcrumb-item"

You can also add the following class to your CSS

.breadcrumb-item::after { 
  content: "/";
}

so you can omit the / in your JSX.

Upvotes: 6

Related Questions