-1

i am getting error at this.setState({ Url: resp.data.Url }); in GETAPI TypeError: Cannot read property 'setState' of undefined.. Please suggest. The GETAPI is basically used to render the data from the API. My use-case is to store all the URLs in state and pass it to DownloadButton component. I need this URL to download the required data.

The hirerachy of code is invokeGetReport which will give all the reports in array. I have looped in and invoked invokeGetReportDetails method. Under invokeGetReportDetails , i am calling invokeImageURL which will contain the URL'of all the reports. I need to store all the URLs in state & passed it to next component

class GetAPI extends Component {
  constructor(props) {
    super(props);
    this.state = {
      ID: [],
      Url: [],
    };
  }

  componentDidMount() {

    console.log(this.props.exptype);
    var accesstoken;
    let reports = [];

    axios
      .post(API, qs.stringify(requestBody), config)
      .then((result) => {
        console.log(result);
        this.setState({ token: result.data.access_token });
        accesstoken = result.data.access_token;
        console.log("access token ins " + accesstoken);
        invokeGetReport(accesstoken);
      })
      .catch((error) => {
        console.log(error);
        console.log(error.data);
      });

    function invokeGetReport(accesstoken) {
      console.log("access token is " + accesstoken);
      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(test_report_url, config_req)
        .then((resp) => {
          console.log(resp);
          console.log("data id is " + resp.data.Items.length);

          //  debugger;
          for (let i = 0; i < resp.data.Items.length; i++) {
            let reportName = resp.data.Items[i].Name;
            let reportID = resp.data.Items[i].ID;
            console.log("id is : " + reportID + "Report Name : " + reportName);
            invokeGetReportDetails(accesstoken, reportID);
            reports.push(reportID);
          }
          //  invokeGetReportDetails(accesstoken, reports);
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
    }

    function invokeGetReportDetails(accesstoken, reportID) {
      console.log("reportID in detail API is " + reportID);
      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(report_details + reportID, config_req)
        .then((resp) => {
          console.log(resp);
          console.log(
            "report data id is " + resp.data.ExpenseEntriesList.length
          );
          for (let i = 0; i < resp.data.ExpenseEntriesList.length; i++) {
            let expenseEntryId = resp.data.ExpenseEntriesList[i].ReportEntryID;
            let ExpenseTypeName =
              resp.data.ExpenseEntriesList[i].ExpenseTypeName;
            console.log(
              "id is : " +
                expenseEntryId +
                " ExpenseTypeName : " +
                ExpenseTypeName
            );
            invokeImageURL(accesstoken, expenseEntryId);
          }
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
    }

    function invokeImageURL(accesstoken, expenseEntryId) {
      console.log("expenseEntryId in detail API is " + expenseEntryId);

      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(image_url + expenseEntryId, config_req)
        .then((resp) => {
          console.log(resp);
          console.log("URL is " + resp.data.Url);
          this.setState({ Url: resp.data.Url });
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
    }

    console.log("repoddrts sdda " + reports[1]);
    console.log("prop value is " + this.props);
    // console.log("report id " + this.state.ID);
  }

  render() {
   return (
      <div>
        <DownloadButton imageUrl={this.Url} />
      </div>
    );
  }
}

export default GetAPI;
ammy
  • 87
  • 1
  • 2
  • 8
  • It's because the "this" you use to change the state refer to the class scope, not for the function scope in the way you declare it. So for all your different function, try to use an arrow function instead. invokeGetReportDetails = () => {} invokeImageURL = () => {} invokeGetReport = () => {} – William Bridge Jun 01 '20 at 10:28

2 Answers2

1

This error is because this is not referrring to GetAPI component and that's because the way you have nested multiple functions inside componentDidMount function.

Write all those functions outside the componentDidMount function and use arrow functions.

Not related to the error but you are also not updating the state correctly. You are overwriting it instead of appending the new url to the existing list of urls.

Change

this.setState({ Url: resp.data.Url });

to

this.setState({ Url: [...this.state.Url, resp.data.Url] });

Here's the fixed code

class GetAPI extends Component {
  constructor(props) {
    super(props);
    this.state = {
      ID: [],
      Url: [],
    };
  }

  componentDidMount() {
    console.log(this.props.exptype);
    var accesstoken;
    let reports = [];

    axios
      .post(API, qs.stringify(requestBody), config)
      .then((result) => {
        console.log(result);
        this.setState({ token: result.data.access_token });
        accesstoken = result.data.access_token;
        console.log("access token ins " + accesstoken);
        this.invokeGetReport(accesstoken);
      })
      .catch((error) => {
        console.log(error);
        console.log(error.data);
      });

    console.log("repoddrts sdda " + reports[1]);
    console.log("prop value is " + this.props);
    // console.log("report id " + this.state.ID);
  }

  invokeGetReportDetails = (accesstoken, reportID) => {
      console.log("reportID in detail API is " + reportID);
      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(report_details + reportID, config_req)
        .then((resp) => {
          console.log(resp);
          console.log(
            "report data id is " + resp.data.ExpenseEntriesList.length
          );
          for (let i = 0; i < resp.data.ExpenseEntriesList.length; i++) {
            let expenseEntryId = resp.data.ExpenseEntriesList[i].ReportEntryID;
            let ExpenseTypeName =
              resp.data.ExpenseEntriesList[i].ExpenseTypeName;
            console.log(
              "id is : " +
                expenseEntryId +
                " ExpenseTypeName : " +
                ExpenseTypeName
            );
            this.invokeImageURL(accesstoken, expenseEntryId);
          }
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
  }

  invokeImageURL = (accesstoken, expenseEntryId) => {
      console.log("expenseEntryId in detail API is " + expenseEntryId);

      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(image_url + expenseEntryId, config_req)
        .then((resp) => {
          console.log(resp);
          console.log("URL is " + resp.data.Url);
          this.setState({ Url: [...this.state.Url, resp.data.Url] });
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
  }

  invokeGetReport = (accesstoken) => {
      console.log("access token is " + accesstoken);
      const config_req = {
        headers: {
          Accept: "application/json",
          Authorization: "Bearer " + accesstoken,
        },
      };

      axios
        .get(test_report_url, config_req)
        .then((resp) => {
            console.log(resp);
            console.log("data id is " + resp.data.Items.length);

            //  debugger;
            for (let i = 0; i < resp.data.Items.length; i++) {
              let reportName = resp.data.Items[i].Name;
              let reportID = resp.data.Items[i].ID;
              console.log("id is : " + reportID + "Report Name : " + reportName);
              this.invokeGetReportDetails(accesstoken, reportID);
              reports.push(reportID);
            }
          //  invokeGetReportDetails(accesstoken, reports);
        })
        .catch((error) => {
          console.log(error);
          console.log(error.data);
        });
  }

  render() {
   return (
      <div>
        <DownloadButton imageUrl={this.Url} />
      </div>
    );
  }
}

export default GetAPI;
Yousaf
  • 27,861
  • 6
  • 44
  • 69
  • @yousaf- the set state is not working. this.setState({ Url: [...this.state.Url, resp.data.Url] }); – ammy Jun 02 '20 at 12:38
  • what do you mean not working? are you still getting `TypeError: Cannot read property 'setState' of undefined.` – Yousaf Jun 02 '20 at 12:39
  • i do not see error in console but the state is coming blank – ammy Jun 02 '20 at 12:42
  • then your error is fixed. There must be some other problem in your code.. – Yousaf Jun 02 '20 at 12:43
  • Thank you, but i am still stuck with this issue of passing the url to next component – ammy Jun 02 '20 at 12:45
  • you can create another question, answer to this question ha been given and it has solved the error mentioned in your question. – Yousaf Jun 02 '20 at 12:52
0

In the context of your functions there is no 'this'. Move your methods invokeGetReport, invokeGetReportDetails and invokeImageURL to class GetAPI and call them through 'this'.

class GetAPI extends Component {

    componentDidMount() {
        ...
          this.invokeGetReport(accesstoken);
        ...
    }

    invokeGetReportDetails = (accesstoken, reportID) => {
        ...
          this.invokeImageURL(accesstoken, expenseEntryId);
        ...
    };

    invokeImageURL = (accesstoken, expenseEntryId) => {
        ...
         this.setState({ Url: resp.data.Url });
        ...
    };

    invokeGetReport = (accesstoken) => {
        ...
         this.invokeGetReportDetails(accesstoken, reportID);
        ...
    };

    render() {
        ...
    }
}

export default GetAPI;

Or use lambda-functions:

const invokeGetReportDetails = (accesstoken, reportID) => {...}
const invokeImageURL = (accesstoken, expenseEntryId) => {...}

etc

Or call them through fn.call(this)

Michael Mishin
  • 586
  • 2
  • 8
  • Not able to understand, Can you please format the above code and help me out? – ammy Jun 01 '20 at 10:39
  • Formatted. Read about javascript closures and 'this'. [this](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/this) and [closures](https://medium.com/javascript-scene/master-the-javascript-interview-what-is-a-closure-b2f0d2152b36) – Michael Mishin Jun 01 '20 at 10:54