2

I have a custom component that requires access to one of my actual component state (rows). I defined it in my constructor but when it should be rendered, this.state seems to be always empty, and i dont know why...

The console always displays "undefined this.state".

Here is my code :

export class TableContainer extends React.Component<any, any> {
    constructor(props : any, context: any) {
        super(props,context);
        this.state = { 
            rows:[ 
                { id: 1, title: 'John', count: 32 },
                { id: 2, title: 'Carl', count: 18 },
                { id: 3, title: 'Mike', count: 25 }
            ]
        };
    }

    rowGetter(i: number) {
        if(this.state) {
            console.log('defined this.state');
            return this.state.rows[i];
        }
        else {
            console.log('undefined this.state');
            return {};
        }
    }

    render() {
        return  (
            <div id="events-container">
                <ReactDataGrid
                    columns={this.state.columns}
                    rowGetter={this.rowGetter}
                    rowsCount={this.state.rows.length}
                    minHeight={500} 
                />
            </div>
        );
    }    
}

I'm new in react so probably it's a little mistake. I have to precise that rowsCount get the good "this.state.rows.length" in the render function.

Thank you by advance

Kamzz
  • 119
  • 4
  • 10
  • See also https://stackoverflow.com/questions/36876105/how-to-properly-deal-with-scope-in-typescript – TSV Jul 26 '17 at 09:24

4 Answers4

3

In this expression rowGetter={this.rowGetter} you are passing your method to ReactDataGrid component. Then it calls rowGetter internally and javascript functions work in such way, that this function this linked to calling object. It means that you have to bind your rowGetter method somehow to your component instance in order to have this linked to it.

There are several approaches:

  • Direct {this.rowGetter.bind(this)} - lacks efficiency as it creates function every time
  • this.rowGetter = this.rowGetter.bind(this) in constructor - verbose way
  • you can rewrite rowGetter method as an arrow function - I think it's the best approach here. It can look like this:

rowGetter = (i: number) => {
  if(this.state) {
    console.log('defined this.state');
    return this.state.rows[i];
  }
  else {
    console.log('undefined this.state');
    return {};
  }
}

Now this linked to parent context and this is your component instance - precisely what you need.

lunochkin
  • 684
  • 4
  • 17
1

The function is being called from another context. You shoud bind function to the correct context:

rowGetter={this.rowGetter.bind(this)}

or declare the function via "fat arrow":

rowGetter = (i: number) => {
    if(this.state) {
        console.log('defined this.state');
        return this.state.rows[i];
    }
    else {
        console.log('undefined this.state');
        return {};
    }
}
TSV
  • 7,538
  • 1
  • 29
  • 37
1

You need to bind you method to this in you constructor:

    constructor(props : any, context: any) {
        super(props,context);
        this.state = { 
            rows:[ 
                { id: 1, title: 'John', count: 32 },
                { id: 2, title: 'Carl', count: 18 },
                { id: 3, title: 'Mike', count: 25 }
            ]
        };
this.rowGetter = this.rowGetter.bind(this);
    }
FrenchTechLead
  • 1,118
  • 3
  • 13
  • 20
1

You can have 2 choose.

  1. Use bind(this)

    this.rowGetter.bind(this)
    

2.Use arrow-function then you don't want to use bind

Voi Mập
  • 779
  • 3
  • 7
  • 22