Consider this basic custom element:
class XElement extends HTMLElement {
constructor() { super(); }
foo() { console.log( this ); }
} customElements.define( 'x-element', XElement );
Here is the problem:
const xelem = new XElement();
/* `foo` will lose its binding to `xelem`:
*/ someButton.onclick = xelem.foo;
// These will work, but it's too verbose:
someButton.onclick = () => xelem.foo();
someButton.onclick = xelem.foo.bind( xelem );
I see only one solution is to add foo as arrow function in constructor, but it seems to me wrong.
constructor() {
super();
this.foo = () => console.log( this );
}
Is there any right way to create method that will never lose its binding?
That is how JavaScript this binding works.
You can read this: THIS (YDKJS)
Basically, the value of this inside a function depends upon how that function is invoked. So you need to explicitly hard bind the this value to your function foo by using the bind() method or defining foo as arrow function (arrow functions lexically bind their context).
So the solution is what you found.
You can do:
In your constructor:
class XElement extends HTMLElement {
constructor() {
super();
this.foo = this.foo.bind(this);
}
foo() { console.log( this ); }
}
Or (I don't like this one)
class XElement extends HTMLElement {
constructor() {
super();
this.foo = () => console.log(this);
}
}
Or
class XElement extends HTMLElement {
constructor() { super(); }
foo = () => { console.log( this ); }
}
Related
When I define a custom element like this:
customElements.define(
"my-tag",
class extends HTMLElement {
constructor() {
var data = {};
...
When I use the element multiple times, like:
<div>
<my-tag yadda="yadda"></my-tag>
<my-tag yadda="yadda2"></my-tag>
</div>
I notice that 'data' is shared among all the instances of 'my-tag'.
I also tried creating as a property, like:
customElements.define(
"my-tag",
class extends HTMLElement {
constructor() {
this.data = {};
...
But still got the same shared memory across the instances.
What am I missing?
Every Custom Element has its own scope:
<my-tag id="ONE"></my-tag>
<my-tag id="TWO"></my-tag>
<script>
customElements.define(
"my-tag",
class extends HTMLElement {
constructor() {
super() // sets AND returns this scope
.data = Math.random(); // chain on super, just because we can
console.log("constructor", this);
}
connectedCallback() {
console.log("connected" , this.id, this.data, this);
}
})
</script>
This question should be an easy riddle for TypeScript/React hackers.
I have a React component that passes a class-object to a child-component.
Within the child-component, I call a method on the class-object.
Those two components look as follows:
class ParentComponent extends React.Component<{}, Foo> {
constructor(props: any) {
super(props);
this.state = new Foo();
}
render() {
return (<ChildComponent {...this.state} />);
}
}
class ChildComponent extends React.Component<Foo, {}> {
render() {
this.props.fooMethod(); // TypeError or not? fooMethod is not a function?
return (<div/>);
}
}
Furthermore, I have two different implementations of Foo.
One of them works, whereas the other one throws a TypeError in the child-component.
Can you explain why only one of those Foo implementations works?
First Foo implementation:
class Foo {
constructor() {
this.fooMethod = function(): void {};
}
fooMethod: () => void;
}
Second Foo implementation:
class Foo {
fooMethod(): void {};
}
Actually, the problem has nothing to do with React.
What is happening is that those two implementations behave slightly different one from each other.
The following code:
class Foo {
constructor() {
this.instanceMethod = function(): void {};
}
fooMethod: () => void;
}
const fooInstance = new Foo();
Declares a class with an instance method instanceMethod.
The following:
class Foo {
prototypeMethod(): void {};
}
const fooInstance = new Foo();
Declares a class with a prototype method prototypeMethod.
When you use object destructuring syntax {...this.state} only own properties and methods (non prototype) are assigned.
So that is the reason why the first implementation works while the second throws an error.
Wonder if anyone can help?
I'm trying to call a parent's constructor or at a minimum get a reference to the parent class in some way without hardcoding anything.
class A {
static foo(options) {
parent::__construct(options); <- this is how you would get the parent in php
}
}
class B extends A {
}
Is this possible?
In a javascript class (and OOP in general), a static method is not part of an instance and therefore the object it resides in does not have a constructor.
You should avoid using static method for this sort of thing and use a standard constructor and call super() to call the parent constructor.
class A {
constructor(options) {
console.log('Options are:');
console.log(options);
}
}
class B extends A {
constructor(options) {
super(options);
}
}
const item = new B({item1: 'abc'});
Further reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/super
You can use super() to call parent constructor
class A {
constructor() {
console.log('I\'m parent ');
}
foo(){
console.log('Class A: Called foo');
}
}
class B extends A {
constructor() {
super();
}
foo(){
super.foo()
}
}
const b = new B();
b.foo();
class PathController {
constructor(){
}
getMainPage(){
alert("getMainPage");
}
setPushState(){
alert("setPushState");
}
}
class MainMenu extends PathController {
constructor (){
// call my PathController here
super();
getMainPage();
setPushState();
}
}
let aMainMenu = new MainMenu();
my intention is to call my getMainPage and setPushState at my MainMenu constructor , i tired this.getMainPage and this.setPushState and it is not working as well. can anyone tell me how to call it ?
Your super is your "this" since we are currently in the constructor. Here's how it should look:
class PathController {
constructor(){
}
getMainPage(){
alert("getMainPage");
}
setPushState(){
alert("setPushState");
}
}
class MainMenu extends PathController {
constructor (){
// call my PathController here
super();
super.getMainPage();
super.setPushState();
}
}
let aMainMenu = new MainMenu();
However once you are outside of the constructor, then you would use "this.getMainPage();"
One approach would be to pass property names to super() which call the functions at PathController parent constructor
class PathController {
constructor(fromMainMenu, ...props) {
if (fromMainMenu) {
for (let fn of props) {
this[fn]()
}
}
}
getMainPage(){
alert("getMainPage");
}
setPushState(){
alert("setPushState");
}
}
class MainMenu extends PathController {
constructor () {
// call my PathController here
super(true, "getMainPage", "setPushState");
}
}
let aMainMenu = new MainMenu();
I got the following component written in typescript. (type definitions from definitelytyped.org). I got the onWheel event bound to a function. But when ever it is fired this is undefined, so how am I supposed to access the referenced element this.div and if I would want/need to change the state how should do that?
import React = require('react');
interface fooProps {
src: string;
}
class foo extends React.Component<fooProps, {}>
{
private div: HTMLDivElement;
public onWheel(e: React.WheelEvent): void {
e.preventDefault();
e.stopPropagation();
//Do stuff with the div, but 'this' is undefined!!
this.div;
}
public render(): JSX.Element {
return (
<div ref={(ref) => this.div = ref} onWheel= { this.onWheel} >
<img src={ this.props.src } />
</div >)
}
}
Don't know about Typescript, but I'm guessing it's the same thing as when creating components using the similar ES2015 syntax which will need a constructor, and function binding to make a reference to this.onWheel work.
So in ES2015,
class foo extends React.Component {
constructor(props) {
super(props);
// Magic happens here:
this.onWheel = this.onWheel.bind(this)
// Now each instance of foo can use this.onWheel
}
onWheel () {
....
}
render (){
....
}
}
Another solution if you don't want to bind each function in the constructor is to use lambdas:
class foo extends React.Component {
constructor(props) {
super(props);
}
// The lambda creates a lexical scope so it's autobound
onWheel = () => {
....
}
render () {
....
}
}
You can read more here.
onWheel= { this.onWheel}
onWheel={this.onWheel.bind(this)}
The simple thing would be converting it into an arrow function which binds automatically:
public onWheel = (e: React.WheelEvent): void => {
e.preventDefault();
e.stopPropagation();
//Do stuff with the div, and yes you can work with 'this' in this function
this.div;
}