0

I'm currently struggling with the following issue:

I have a function call like this:

foo = this.initializeFoo(id, array); // console.log() says: undefined

And the function:

export function initializeFoo(id, array) {
    axios.get(API_URL + '/route/' + id)
        .then(response => {
            let copyPayload = [...response.data.foo];
            let copyArray = [...array];

        // Some operations  
            copyArray = copyArray.filter(x => {
                let exists = false;
                copyPayload.forEach(payload => {
                    if (x.id === payload.id) {
                        x["newAttribute"] = id;
                        exists = true;
                    }
                });
                return exists
            });

            console.log("Returning copyArray", copyArray); // Displays the proper data
            return copyArray;
        })
        .catch(error => {
            this.setState({loaded: false});
            console.log(error);
        })
}

The question is: Why is the console.log() undefined? I guess it has something to do with the way I return the array within the axios call, but I can't think of another way to do it.

I also don't want to use setState within the function, since I call a few initialize functions and I'd rather want to use one setState after I got all the data initialized.

Thanks in advance!

UPDATE

Can I do it this way:

foo["blub"]  = this.initializeFoo(id, array).then(result => {
             return result;
        });
Felix
  • 5,452
  • 12
  • 68
  • 163
  • 3
    Didn't you actually forget to return the axios call? `return axios.get ... `? – Sergeon Nov 21 '17 at 10:28
  • 3
    Possible duplicate of [How to return the response from an asynchronous call?](https://stackoverflow.com/questions/14220321/how-to-return-the-response-from-an-asynchronous-call) – Durga Nov 21 '17 at 10:30
  • if you could use async/await, make the export function `async`, and await the response when assigning foo – Icepickle Nov 21 '17 at 10:32

2 Answers2

3

You need to return the axios.get(API_URL + '/route/' + id) call as the follows

export function initializeFoo(id, array) {
    return axios.get(API_URL + '/route/' + id) // add return here
        .then(response => {
            let copyPayload = [...response.data.foo];
            let copyArray = [...array];

        // Some operations  
            copyArray = copyArray.filter(x => {
                let exists = false;
                copyPayload.forEach(payload => {
                    if (x.id === payload.id) {
                        x["newAttribute"] = id;
                        exists = true;
                    }
                });
                return exists
            });

            console.log("Returning copyArray", copyArray); // Displays the proper data
            return copyArray;
        })
        .catch(error => {
            this.setState({loaded: false});
            console.log(error);
        })
}

However, the function will now return a promise. You will have to do Something like:

return this.initializeFoo(id, array).then(result => {
    foo = result;
    console.log(foo)
}); // console.log() says: undefined

Or you can use async/await

Ayush Gupta
  • 8,716
  • 8
  • 59
  • 92
1

You need to return from the initializeFoo method but that would return a Promise of the data and not the data itself.

To get the data:

this.initializeFoo(..args).then(copyArray => /* do something with the array */)

Update:

A Promise is a neat way to handle asynchronous activity. When you attach the then to a Promise, you're essentially saying "As soon as this promise resolves THEN execute the following block"

let result = this.initializeFoo(..args).then(copyArray => copyArray)
// if you were to inspect `result` here, it would also be a `Promise`!

If you still do not use .then, you could make your function async. This will allow you to use await in your function and you could essentially write code as if it were synchronous.

async function initializeFoo(id, array) {
    let response = await axios.get(`${API_URL}/route/${id}`);
    // some operations on response
    let copyArray = this.massageResponse(response, array);

    // don't catch any exceptions here, let it bubble out...
    return copyData;
} 

Now, since you didnt want to setState within the method, you'll have to do it from the place you called it.

class MyComponent extends React.Component {

  componentDidMount() {
    // time to initialize data for this component
    this.initializeComponentData();
  }

  async function initializeComponentData() {
    // this function is called from `componentDidMount`
    try {
      let promiseOfBar = this.initializeFoo(id, array);

      // call another initializer 
      // - (this would have to be an async function as well)
      let promiseOfBaz = this.anotherInitalizer(id, array);  

      let foo = {};
      let response = await Promise.all(promiseOfBar, promiseOfBaz);
      foo['bar'] = response[0];
      foo['baz'] = response[1];

      // finally call `setState`
      this.setState({ ...foo, loaded: true });
    } catch (exception) {
      // catch any possible exceptions and `setState` accordingly
      this.setState({ loaded: false });
    }
  }

  // rest of the component 

  render() {
    // render logic
  }
}
Raghudevan Shankar
  • 1,083
  • 9
  • 18
  • Thanks! I've updatet my initial question. Can you maybe have a look? Thanks in advance – Felix Nov 21 '17 at 12:36
  • thanks. Is there another way to return the data out of the promise then block? – Felix Nov 21 '17 at 13:08
  • You can change global variables or do `this.foo['bar'] = copyData` from within the `then` block but I wouldn't recommend that since you want to control what you do when. What I mean by that is that 1. you make call to a service to retrieve data 2. On retrieving the data you want to store that data into your component's state 3. On state change you want to render your component in a particular way. Now if you set a global variable, you wouldn't be able to keep track of when that global variable changes/changed. – Raghudevan Shankar Nov 21 '17 at 13:13
  • when I do this within the `then` block its not accessable outside `undefined` – Felix Nov 21 '17 at 13:14
  • its not a global var, just in scope of a function. And setstate is called later. – Felix Nov 21 '17 at 13:18
  • It wouldn't be accessible outside immediately because you're dealing with asynchronous activity. When you do `axios.get` it does not return the data immediately but instead returns a `Promise` of the data; the request goes over the network, fetches the resource and brings it back to your browser. When the browser has the data, it will then resolve the `Promise` that it returned to you with the data that it just fetched. _**THEN**_ you can decide what you want to do with that data. – Raghudevan Shankar Nov 21 '17 at 13:19