This context in arrow function ReactJS

278 views Asked by At

I have a react component which looks like this.

import React, { PropTypes, Component } from 'react';
import { Accordion, Panel, PanelGroup, Table } from 'react-bootstrap';


const FormCell = ({ data }) => (
  <div>
    <a className="pdf-name" onClick={this.doSomething}>{data.item.extension.nameCodeDes}</a>
  </div>
);

class Docs extends Component {
  constructor(props) {
    super(props);
    this.doSomething= this.doSomething.bind(this);
  }

  doSomething() {
    setTimeout(() => {
      console.log("here we are");
    })
  }

  // ...
}

Docs.defaultProps = {
  tableData: null,
  cols: null,
  metaData: {
    documentsColumnMetaData: [
      {
        displayName: 'Policy/Account',
        columnComponent: {
          component: FormCell,
          actions: [],
        },
      },
    ],
  },
};

export default Docs;

The this.doSomething is transpiled to undefined.doSomething in dev tools. I get an error Cannot read property 'downloadDocument' of undefined. Can someone please let me know what I'm missing here? P.S FormCell does more that what is posted. I reduced the code for simplicity

3

There are 3 answers

0
DiverseAndRemote.com On BEST ANSWER

This looks like you're doing it wrong but I'll still answer your question directly. The issue here is that you are defining FormCell outside of Docs but you expect this to be an instance of Docs. What you can do is

change it to:

const FormCell = ({ data, doSomething }) => (
    <div>
        <a className="pdf-name" onClick={doSomething}>{data.item.extension.nameCodeDes}</a>
    </div>
);

And then I assume that you omitted some code inside the Docs component that looks like this

const ColumnComponent = this.props.metaData.documentsColumnMetaData[ 0 ].columnComponent.component;
return <ColumnComponent data={ someData } doSomething={ this.doSomething } />;

Notice how I added the doSomething={ this.doSomething }. That will allow you to pass the doSomething method to the FormCell component as a prop.

3
Andy Ray On

Arrow functions do not have a this, instead this falls through to the upper scope. I'm not sure why it's undefined in this specific case, but this isn't territory you want to be in normally. Looking at this code I would guess this would be the global object, but maybe a top level arrow function referencing this is supposed to behave this way.

If you want to bind the function, define it with the function keyword instead.

function FormCell({ data }) {
    return <div>
        <a className="pdf-name" onClick={this.doSomething}>{data.item.extension.nameCodeDes}</a>
    </div>;
}
3
Kryten On

To expand on the comment I made above...

You've got two different components in your code example: FormCell and Docs. There appears to be no relationship between them, except that you're referring to an undefined this.doSomething function in the FormCell component, which happens to have the same name as a doSomething function declared as a method on the Docs component. This leads me to think that they are supposed to be the same function.

So, working from that assumption...

The this.doSomething function referred to in FormCell should refer to a FormCell.doSomething function. After all, this.doSomething is being called from within the function that's rendering a FormCell component - it should refer to FormCell then. But you haven't defined a doSomething function on the FormCell component, so there's nothing for it to refer to (and you should be getting a "doSomething is not a function" or "doSomething is undefined" error).

If it is your intention to have the this.doSomething in the FormCell refer to the doSomething function on an instance of the Docs component, then you'll need to pass that function in to the FormCell component as a prop.