Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add docs for using Promises and making AJAX requests #531

Closed
wants to merge 9 commits into from
57 changes: 57 additions & 0 deletions template/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Adding Bootstrap](#adding-bootstrap)
- [Adding Flow](#adding-flow)
- [Adding Custom Environment Variables](#adding-custom-environment-variables)
- [Fetching AJAX Requests](#fetching-ajax-requests)
- [Integrating with a Node Backend](#integrating-with-a-node-backend)
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
- [Adding `<meta>` Tags](#adding-meta-tags)
Expand Down Expand Up @@ -463,6 +464,62 @@ if (process.env.NODE_ENV !== 'production') {
}
```

## Fetching AJAX Requests

This project includes a [fetch polyfill](https://github.com/github/fetch), which makes it easier to make web requests.

The `fetch` function allows to easily makes AJAX requests. It takes in a URL as an input and returns a `Promise` that resolves to a `Response` object. You can find more information about `fetch` [here](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's say "the global fetch function" instead to make it clear you don't need to import it.



__About Promises:__ This project also includes a [Promise polyfill](https://github.com/then/promise) which provides a full implementation of Promises/A+. A Promise represents the eventual result of an asynchronous operation, you can find more information about Promises [here](https://www.promisejs.org/) and [here](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise).

You can make a GET request like this:
```javascript
class MyComponent extends Component {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's add all necessary imports here so people can copy and paste. Same in next block.

Copy link
Contributor

@gaearon gaearon Sep 4, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we make this a drop-in replacement for App.js that ships with this project? Instead of UserGists maybe map over their names in a <ul>.

state = {}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's not use class properties here yet. People might get confused. We'll document them later.


componentDidMount() {
this.fetchGists();
}

fetchGists() {
fetch('https://api.github.com/users/octocat/gists')
.then((response) => response.json())
.then((gists) => this.setState({ gists }))
.catch((error) => console.log('Error', error));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a subtle problem here. If setState() throws (for example due to an error in render()), we will get into the catch handler. It is then easy to miss or ignore that error, and now React is in an inconsistent state.

Instead, let's put catch() before the second then(). This way we only catch network errors. We want errors in setState() to stay unhandled.

Let's make catch() return an empty array. Then we can safely use its result in setState() whether it succeeded or failed.

Copy link
Contributor

@fson fson Sep 6, 2016

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also note that fetch doesn't reject/throw for an error status, so if GitHub is down and you get a 503 status, or if you get rate limited and they return 403, none of these errors will end up in the catch() – setState() gets called with the parsed JSON body of the error response.

So to handle HTTP errors, you'll also have to check response.status, e.g. response.status >= 200 && response.status < 300 (response.ok is a handy shortcut for that), and treat a non-OK status as an error.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yep, @insin mentioned this later in a comment too. We should handle both !ok and actual errors in the same way, that is, by showing the message.

}

render() {
return <UserGists gists={this.state.gists} />;
}
}
```

You can also use the `async/await` syntax to fetch data. [Here](https://zeit.co/blog/async-and-await) is an introduction to it.
```javascript
class MyComponent extends Component {
state = {}

componentDidMount() {
this.fetchGists();
}

async fetchGists() {
try {
const response = await fetch('https://api.github.com/users/octocat/gists');
const gists = await response.json();
this.setState({ gists });
} catch(error) {
console.log('Error', error);
}
}

render() {
return <UserGists gists={this.state.gists} />;
}
}
```

## Integrating with a Node Backend

Check out [this tutorial](https://www.fullstackreact.com/articles/using-create-react-app-with-a-server/) for instructions on integrating an app with a Node backend running on another port, and using `fetch()` to access it. You can find the companion GitHub repository [here](https://github.com/fullstackreact/food-lookup-demo).
Expand Down