简体   繁体   中英

Undefined object returned from function when called within another function

I have a function called request that makes an AJAX call to an API, and returns some JSON data. RenderEntry creates an Entry component, which is basically just a div with an h3 element. The h3 element is supposed to come from the JSON data that is returned from request .

In RenderEntry , I am trying to call request with the id that is passed into it, assign it to the constant data , and then pass data.name into the Entry as a prop. data , however, is undefined.

In truth, this is my fist React app that I bootstrapped with create-react-app . I know for a fact that request works when you pass a literal id into it, but I do not understand why it's not working in this function.

function Entry(props) {
  return (
    <div>
      <h3>{props.name}</h3>
    </div>
  );
}

class App extends Component {

  RenderEntry(id) {
    const data = request(id);

    console.log(request(id));  // undefined
    console.log(data);         // undefined

    return <Entry name={data.name}/>
  }

  render() {
    return (
      this.RenderEntry(8000)
    );
  }
}


function request(id) {
  const base = 'https://url.com/'
  const request = new XMLHttpRequest();

  request.open('GET', base + id, true);

  request.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      // Success!
      const data = JSON.parse(this.response);
      // console.log(data);
      return data;
    } else {
      console.log('We reached our target server, but it returned an error!');
    }
  };

  request.onerror = function() {
     console.log('There was a connection error of some sort');
  };

  request.send();
}

The way to think of send, onload and onerror in an XMLHttpRequest is that the send initiates a request to get information. This information will return some time later. Meantime, the function it is in completes and since you don't have a return, it defaults to returning 'undefined'. That is why you get 'undefined' from the console.log(request(id)) call.

When the data is finally retrieved, either the onload() or onerror() function is called (the former if the retrieval was successful and the latter if an error occurred. These functions are called from within the XMLHttpRequest not from your code and so the return in the onload() is not seen by your code.

In order to get the data, you will need to provide a function that will be called when the data finally arrives - this is called a 'callback' function and it refers to any function that is invoked after an asynchronous operation (one that is triggered but responds some time later).

Your request() function requires some minor changes like so:

function request(id, callback) {
  const base = 'https://url.com/'
  const request = new XMLHttpRequest();

  request.open('GET', base + id, true);

  request.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      // Success!
      const data = JSON.parse(this.response);
      // console.log(data);
      callback(data);
    } else {
      console.log('We reached our target server, but it returned an error!');
    }
  };

  request.onerror = function() {
     console.log('There was a connection error of some sort');
  };

  request.send();
}

Now to call this function, you can do something like this:

function showData(data) {
  console.log(data);
}
request(id, showData);

That should display your data in the console.

I am not overly familiar of the details of how React works, so I am sure there are more capable folks to help with this portion of the answer. If not, and you are still stuck let me know and I will do what I can to clarify further.

It is not unusual to either pass two callback functions to the function managing the XMLHttpRequest (request() in your case) - one is called if the data is successfully retrieved and the other if an error occurs. Something like this:

function showData(data) {
  console.log(data);
}
function reportError(errResponse) {
  console.warn(errResponse);
}
request(id, showData, reportError);

Or to pass one callback that takes two parameters, one for errors and one for the data if successful. Something like this:

function receiveData(errResponse, data) {
  if (errResponse) {
    console.warn(errResponse);
  }
  else {
    console.log(data);
  }
}

You would then need to adjust your request() function to invoke the callback function with the appropriate parameters:

  callback(null, data);   // if you get the data successfully

  callback("Unable to retrieve data");  // if you get an error

Here's a link with more information on getting data from an XMLHttpRequest, and another on getting information on the difference between asynchronous and synchronous function calls.

That's how the code should be :

function Entry(props) {
  return (
    <div>
      <h3>{props.name}</h3>
    </div>
  );
}

class App extends Component {
  constructor(...args){
    super(...args)
    this.state = {}
    request(8000);
  }

  render() {
    if(this.state.error) return (..error view)
    if(!this.state.data) return (..empty view of loader)
    return (<Entry name={this.state.data.name}/>)
  }
}


function request(id) {
  const base = 'https://url.com/'
  const request = new XMLHttpRequest();

  request.open('GET', base + id, true);

  request.onload = function() {
    if (this.status >= 200 && this.status < 400) {
      // Success!
      const data = JSON.parse(this.response);
      // console.log(data);
      this.setState({data:data})
      return data;
    } else {
      console.log('We reached our target server, but it returned an error!');
      this.setState({error:'We reached our target server, but it returned an error!''})
    }
  };

  request.onerror = function() {
     console.log('There was a connection error of some sort');
     this.setState({error:'There was a connection error of some sort'})
  };

  request.send();
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM