This lesson is for PRO members.

Unlock this lesson NOW!
Already subscribed? sign in

Building a React.js App: Making Server Requests in React with Axios

9:30 React lesson by

In this lesson, we’ll walk through implementing Axios into our application in order to communicate with the Github API.

Get the Code Now
click to level up

egghead.io comment guidelines

Avatar
egghead.io

In this lesson, we’ll walk through implementing Axios into our application in order to communicate with the Github API.

Avatar
Daniel

Shouldn't the getGithubInfo() call be in componentWillReceiveProps as well? else the request to github API and update of the user info doesn't update until a page refresh.

Should it be within componentWillReceiveProps? Or am I missing something?

In reply to egghead.io
Avatar
Philip

Hi, I am getting an error. helpers.getGithubInfo is not a function? I have gone over the code like for like I am certain, pretty weird :)

In reply to egghead.io
Avatar
Tyler

Put your code up on Github and send me the URL. I'll take a look when I get a second.

In reply to Philip
Avatar
Philip

Thank you for the offer Tyler. I have it working now. Just did a copy/paste :(
Pretty sure the code was like for like, haha.
One of the many wonders of programming I guess :)

In reply to Tyler
Avatar
Tyler

You're correct. Now sure why I missed that.

In reply to Daniel
Avatar
Richard

Just working with the data returned from axios, and noticing issues when passing around objects in React. Is there any reference to this issue somewhere?

I am just trying to display a single property of the bio object returned from github, I can view it in the React console, however, not the on the html page. Any ideas?

So the repos display (its an array), but the bio does not display, and I notice a console error complaining about working with the object.

Avatar
Tyler

Hi Richard,

What's the exact error message you're getting in the console?

In reply to Richard
Avatar
Manny

Hey Tyler,

I'm receiving the following error after searching for a github user:

Uncaught Error: Invariant Violation: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of UserProfile.

Below is a link to the repo for my code:

https://github.com/yooomanny/github-app

Avatar
Joel

Hey Tyler,

I'm receiving the following error after searching for a github user:

Uncaught Error: Invariant Violation: Objects are not valid as a React child (found: object with keys {}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of UserProfile.

Below is a link to the repo for my code:

https://github.com/yooomanny/github-app

Repos.js is likely cause of error. React expects the array to be an array or elements versus objects.

render: function() {
+ const repos = this.props.repos.map(repo => <div>{repo.name}</div>)
render (
In reply to Manny
Avatar
Manny

Thanks Joel.

I'm a bit confused with your code. Am I supposed to add the green line after line 8 in my repo.js file? If so, I just gave it a shot and received the same error.

By the way, I had the same error when I had " bio: {} " in my profile.js file, but it went away after setting it to an array and requiring it as an array for the mean time. How can I allow for objects in these instances?

In reply to Joel
Avatar
Joel

Thanks Joel.

I'm a bit confused with your code. Am I supposed to add the green line after line 8 in my repo.js file? If so, I just gave it a shot and received the same error.

By the way, I had the same error when I had " bio: {} " in my profile.js file, but it went away after setting it to an array and requiring it as an array for the mean time. How can I allow for objects in these instances?

I'm just guessing. You have to add JSX elements as children, not JavaScript objects/arrays of objects. I'm not actually running and debugging your repo.

In reply to Manny
Avatar
Manny

Gotcha, thanks.

In reply to Joel
Avatar
egghead.io

The lesson video has been updated!

Avatar
Alan

Thanks for the video! Question -- how were we able to make an API request to Github's API without generating an access token? Does axios somehow bypass that?

Avatar
Tyler

Hi Alan,

Github allows you to make a certain number of requests without an API key. Once you pass that amount (which is quickly if anyone is actually using your app), then you get rate-limited.

In reply to Alan
Avatar
Alan

Gotcha, that makes sense. I hit my limit -- it was easy to do when I'm using a hot loader!

On an unrelated note, when I try to make a call to http://www.bayareabikeshare.com/stations/json, I get an Access-Control-Allow-Origin error. I'm guessing this has to do with some cross domain issue. Why were we able to make an API call to Github but not a call to any JSON endpoint?

In reply to Tyler
Avatar
Tyler

Another great observation. The Github... "API supports Cross Origin Resource Sharing (CORS) for AJAX requests from any origin." (https://developer.github.com/v3/) Where as bayareabikeshare obviously does not.

In reply to Alan
Avatar
Divyendu

Hey Tyler,
Good video, continuing to talk about

https://github.com/tylermcginnis/github-notetaker-egghead/blob/09-axios/app/utils/helpers.js

What if the promises are interdependent. In this case, the both calls required username as parameter, but how can we use Axios.all when we have two service calls A, B where parameter of B is retrieved from service call A

Avatar
Tyler

Hi Divyendu,

In that case you'd chain your promises rather than using .all. I found this tutorial that does a quick job of explaining how you'd do that - https://html5hive.org/how-to-chain-javascript-promises/.

In reply to Divyendu
Avatar
Huy

Invalid prop bio of type object supplied to UserProfile, expected array. Check the render method of Profile.

Here are my codes:

getInitialState: function(){
return {
bio: {},
repos: [],
notes: []
};
},
// UserProfile file
propTypes: {
username: React.PropTypes.string.isRequired,
bio: React.PropTypes.object.isRequired
},

Please help me to solve this. Thank you in advance !

Avatar
Emily

I'm noticing that in Profile.js, in the componentDidMount function, 'this.props.username' is not defined, I had to instead pass in 'this.props.params.username'. Could this have something to do with the context of 'this' at point? I did include .bind(this) so I'm not sure what's going on :/

Avatar
Emily

Oops nevermind! I see that using 'his.props.params.username' is actually what is intended to be there... realizing username isn't even in props

In reply to Emily
Avatar
Dane

this lesson was beautiful, great job.

Avatar
Jun

I got all the code running, but found an error from the chrome console. Does anyone have any idea why this can be happening?

Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {id, name, fullname, owner, private, htmlurl, description, fork, url, forksurl, keysurl, collaboratorsurl, teamsurl, hooksurl, issueeventsurl, eventsurl, assigneesurl, branchesurl, tagsurl, blobsurl, gittagsurl, gitrefsurl, treesurl, statusesurl, languagesurl, stargazersurl, contributorsurl, subscribersurl, subscriptionurl, commitsurl, gitcommitsurl, commentsurl, issuecommenturl, contentsurl, compareurl, mergesurl, archiveurl, downloadsurl, issuesurl, pullsurl, milestonesurl, notificationsurl, labelsurl, releasesurl, deploymentsurl, createdat, updatedat, pushedat, giturl, sshurl, cloneurl, svnurl, homepage, size, stargazerscount, watcherscount, language, hasissues, hasdownloads, haswiki, haspages, forkscount, mirrorurl, openissuescount, forks, openissues, watchers, defaultbranch}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of Repos.

Avatar
Jun

I got all the code running, but found an error from the chrome console. Does anyone have any idea why this can be happening?

Uncaught (in promise) Error: Objects are not valid as a React child (found: object with keys {id, name, fullname, owner, private, htmlurl, description, fork, url, forksurl, keysurl, collaboratorsurl, teamsurl, hooksurl, issueeventsurl, eventsurl, assigneesurl, branchesurl, tagsurl, blobsurl, gittagsurl, gitrefsurl, treesurl, statusesurl, languagesurl, stargazersurl, contributorsurl, subscribersurl, subscriptionurl, commitsurl, gitcommitsurl, commentsurl, issuecommenturl, contentsurl, compareurl, mergesurl, archiveurl, downloadsurl, issuesurl, pullsurl, milestonesurl, notificationsurl, labelsurl, releasesurl, deploymentsurl, createdat, updatedat, pushedat, giturl, sshurl, cloneurl, svnurl, homepage, size, stargazerscount, watcherscount, language, hasissues, hasdownloads, haswiki, haspages, forkscount, mirrorurl, openissuescount, forks, openissues, watchers, defaultbranch}). If you meant to render a collection of children, use an array instead or wrap the object using createFragment(object) from the React add-ons. Check the render method of Repos.

Avatar
Jun

Got it. This error is caused by the line "REPOS: {this.props.repos}", which I did not delete...

In reply to Jun
Avatar
Bharat Soni

I'm getting react-router error,

the `History` mixin is deprecated, please access `context.router` with your own `contextTypes`.

I know it is something to do with new react-router, but i couldn't fix it. Could you please guide...

Our app is coming together very nicely, but one thing we need to do now is we need to go and fetch the data from github in order to populate both of these components. What we're going to use is this tool called Axios. Axios allows us to make http requests or network requests in its promise space, so it's really nice. If you're coming from an Angular background it's very similar to $HTTP in Angular. Head over to your terminal and go ahead and run npm install axios.

terminal

$ npm install axios

Now once that is done, let's go ahead and we're going to make a helper file for us that is going to handle our network request. So let's go ahead and make a new folder called utils, inside this utils folder go ahead and make a file called helpers.js. In this file we're going to go ahead and require Axios,

utils/helpers.js

var axios = require('axios');

and we're really concerned about two things, we're concerned about getting the users repos, and getting their profile.

So let's go ahead and make two functions, the first one let's called getRepos, it takes in a username and it's going to return whatever axios.get returns us, and we're going to hit this endpoint and get that user's /repos.

utils/helpers.js

function getRepos(username){
  return axios.get('https://api.github.com/users/' + username + '/repos');
};

So if you're not familiar with promises, don't worry about it too much right now, we'll talk a little bit more about promises here in a little bit. For now, let's just create another function called getUserInfo that takes in a username and it's going to return us whatever axios.get returns us, and we're going to append username.

utils/helpers.js

function getuserinfo(username){
  return axios.get('https://api.github.com/users/' + username);
};

In a nutshell this is how promises work. If I were to invoke the getRepos function, let's say I pass in my github username, that's going to return us this promise object, OK? This promise object then has a .then property on it, and the function that we pass to .then, the callback function, is going to get invoked whenever this promise gets resolved.

utils/helpers.js

var promisObj = getRepos('tylermcginnis');
promisObj.then(function(data){
  console.log(data);
});

So basically the way this will work is we call getRepos, that returns us a promise that says, "OK, when we go and we fetch the data from this API, when that data is back, go ahead and invoke this callback function and then console.log(data)." So it's really convenient, it kind of gets around the idea of having callbacks and callback hell and all that stuff. But the way we're going to use this is actually a little bit more advanced.

We're going to create an object that we're going to export, and a property on this object is going to be called getGitHubInfo, because if you think about what we're doing, we basically need both of these functions to be invoked at the same time, and when we get the data back from both of them, when we have the repos and when we also have the user information, we then need to render the component and show all that data to the view.

utils/helpers.js

var helpers = {
  getGitHubInfo: funtion() {

  }
}

module.exports = helpers;

What we're going to do is we're going to use a feature of Axios called axios.all. Let's make sure we passed in our username here first. All right, so what axios.all does is it takes in an array of promises, so let's invoke getRepos, and let's also invoke getUserInfo.

So then what we can do is that will return us a promise which we pass a callback function to, and now what's going to happen is instead of just waiting for one promise to be resolved and then this function will be invoked, what happens is axios will wait for both of these promises to be resolved, and then it will pass us an array of data we got back from both of these invocations.

utils/helpers.js

var helpers = {
  getGitHubInfo: function(username){
    return axios.all([getRepos(username), getuserinfo(username)])
    .then(function(arr){

    });
  }
};

So it's really convenient because we can make these requests the exact same time, and when both of them are ready, this function will run passing us that array. So let's go ahead from here all we want to do is return an object that has a repos property and it returns us an array, the very first property in this array is going to be the repos, the users repositories because that's the first item in the array that we passed axios, and the second property in this object is going to be bio and it's going to be at the first index.

utils/helpers.js

.then(function(arr){
  return {
    repos: arr[0].data,
    bio: arr[1].data
  }
});

So again, to recap what's going to happen here is when we invoke getGitHubInfo we pass it a username, we then will invoke axios.all and pass it two promises. When both of these promises resolve, or when this data has gotten back from github, both of these pieces of data, this function's going to run and it's then going to pass us back an object with our repos and with our bio.

So now let's go ahead and head over to our profile view or profile component and let's utilize this. So let's go ahead first and we're going to require helpers, and I believe we need to go back a folder and then into utils, and then to helpers,

components/Profile.js

var helpers = require('../utils/helpers.js');

and then let's go ahead and make it so when this component mounts, after it sets up the Firebase stuff, let's go ahead and invoke helpers.getGitHubInfo.

We're going to pass it the username from the route params and that will return us a promise which we then can pass a function which will get invoked when our data object is ready or when our github data is ready. Once it's ready we can call this.setState and we set the bio to the bio object and we set the repos property to the repos object.

components/Profile.js

componentDidMount: function(){
  this.ref = new Firebase('https://github-note-taker.firebaseio.com/');
  var childRef = this.ref.child(this.props.params.username);
  this.bindAsArray(childRef, 'notes');

  helpers.getGitHubInfo(this.props.params.username)
    .then(function(data){
      this.setState({
        bio: data.bio,
        repos: data.repos
      })
    }.bind(this))
},

All right, so there's one little gotcha in here, if you're familiar with the this keyword in JavaScript you already know that this keyword is different than this keyword, and that sounds a little bit weird, but basically whenever you go inside of a new function a new context is created, and so this keyword is going to be a little bit different than this keyword.

components/Profile.js

this.bindAsArray(childRef, 'notes');

helpers.getGitHubInfo(this.props.params.username)
  .then(function(data) {
    this.setState({ ... })
  })

So what we need to do as our goal is to basically make it so this keyword is the same as this keyword because if it's not, then this one isn't going to have a setState property. So one trick you can do is here, you can use .bind(this) and in a nutshell what .bind does, it returns you a new function and you're allowed to specify the context of that new function, so all this is doing is it's saying, "Hey, return me a new function with a context, or with the this keyword inside of that function referring to this keyword instead of this keyword inside of that."

components/Profile.js

helpers.getGitHubInfo(this.props.params.username)
  .then(function(data) {
    this.setState({ ... })
  }.bind(this))

That sounds confusing, I know, but if that's still confusing go ahead and look up how the this keyword works and you will be entertained for days. One note really quick about the this keyword, if you're still confused I've made three lessons and they're actually my favorite Egghead lessons I think I've made on the this keyword. So if you're still confused go ahead and check out this playlist, and it should help you out a little bit.

All right, so if this worked correctly what's going to happen is when the component mounts it does some Firebase stuff, it goes and gets our github information, gets the data resets the state and changes our bio and our repos property. We can clear these out now too, and then as of React .14 we can no longer just get JSON and show it to the view so what we're going to do is instead of just spitting that all in the view, let's go ahead and just console.log(this.props.repos) so we can see what that is.

components/Profile.js

componentDidMount: function(){
  this.ref = new Firebase('https://github-note-taker.firebaseio.com/');
  var childRef = this.ref.child(this.props.params.username);
  this.bindAsArray(childRef, 'notes');

  helpers.getGitHubInfo(this.props.params.username)
  .then(function(data) {
    this.setState({ 
      bio: data.bio,
      repos: data.repos
    })
  }.bind(this))
},

Make sure everything's working, and then same thing in our userProfile component let's go ahead and remove bio and instead just console.log('BIO: ',this.props.bio). All right, webpack is running, let's see if this works. So we should see here is two responses, we also have propType, that's fine for now. So bio.data, we have all our data and then repos.data here are all our repositories.

Finished

So now one more thing, let's go ahead and make it so we don't get the whole response object, but we just get the data. So inside of our helpers instead of returning just the first item, let's go ahead and return the first item with data then .data here as well. So that should give us just this data properties Check it out, and there we go, there's our bio and there are our repos.

HEY, QUICK QUESTION!
Joel's Head
Why are we asking?