javascriptreactjssharepointsharepointframework

SharePoint Framework React Button call within grid (loop)


I have a web part that has a button within a grid, which is created via a mapping function. First thing I do is bind:

    constructor(props: IPageApprovalProps) {
       super(props);
       this._handleClick = this._handleClick.bind(this); 
     }

My event handler, for now, just logs to the console

private _handleClick() {
    console.log('fired!');

  }

Finally, here's the render:

public render(): React.ReactElement<IPageApprovalProps> {


     return (
          //code above here
          <button className="ms-Button ms-Button--primary root-82" onClick={() => this._handleClick()}>Approve</button>
          {this.props.approvers.map(function(item,key){ 
          return(<div className={styles.rowStyle} key={key}>
            <div className={styles.CellWideStyle}>
              {item.Name}
            </div>
            <div className={styles.CellWideStyle}>
              {<condition> ? 
                (<button className="ms-Button ms-Button--primary root-82" onClick={() => this._handleClick()}>Approve</button>) : 
                (<image>)
              }
            </div>
      );
}

There are two buttons here - one outside the loop, and one inside the loop. The first fires, but the second one throws:

Uncaught TypeError: Cannot read property '_handleClick' of undefined
    at onClick (page-approval-web-part_d7b27412e020f7f53abca90c271be557.js:1)
    at Object.o (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at Object.invokeGuardedCallback (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:887)
    at Object.invokeGuardedCallbackAndCatchFirstError (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:887)
    at s (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at p (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at m (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at d (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at v (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)
    at y (sp-pages-assembly_en-us_fd7935d40583aa7d6c88123f98f14171.js:879)

Solution

  • In this line:

              {this.props.approvers.map(function(item,key){ 
    

    note the function keyword. this from within a function body does not point to the same this as outside it. That's why you observe different behavior between the two uses of this._handleClick. (further reading)

    The easiest (and probably best) way to resolve this would be to replace function use with an ES6 "arrow function". (further reading) It would look like this:

              {this.props.approvers.map((item,key) => { 
    

    Arrow functions retain the this value of the surrounding lexical scope - meaning that this._handleClick will still be accessible to you from inside the function body.

    Side note: the SPFx toolchain transpiles arrow functions into ES5 functions in the distributables, so you don't have to worry about ES6 compatibility.