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

Conversation

rogeliog
Copy link
Contributor

@rogeliog rogeliog commented Sep 1, 2016

#521 This adds some simple documentation for the fetch and Promise polyfills.

@gaearon Should we also add a simple example of how to use this with React? Or do you think it falls out of scope?

class MyComponent extends Component {
  state = {}

  componentDidMount() {
    fetch('/todos.json')
    .then((response) => response.json())
    .then((todos) => this.setState({ todos: todos }))
    .catch((error) => console.log('Error', error));
  }

  render() {
    return <TodoList todos={this.state.todos} />;
  }
}

@ghost
Copy link

ghost commented Sep 1, 2016

Thank you for your pull request and welcome to our community. We require contributors to sign our Contributor License Agreement, and we don't seem to have you on file. In order for us to review and merge your code, please sign up at https://code.facebook.com/cla - and if you have received this in error or have any questions, please drop us a line at cla@fb.com. Thanks!

@ghost
Copy link

ghost commented Sep 1, 2016

Thank you for signing our Contributor License Agreement. We can now accept your code for this (and any) Facebook open source project. Thanks!

@ghost ghost added the CLA Signed label Sep 1, 2016
@@ -23,6 +23,8 @@ You can find the most recent version of this guide [here](https://github.com/fac
- [Adding Custom Environment Variables](#adding-custom-environment-variables)
- [Integrating with a Node Backend](#integrating-with-a-node-backend)
- [Proxying API Requests in Development](#proxying-api-requests-in-development)
- [Using Promises](#using-promises)
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 make this one section: Fetching AJAX Requests. Let’s also put it before Integrating with a Node Backend.

@ghost ghost added the CLA Signed label Sep 2, 2016
@gaearon gaearon mentioned this pull request Sep 2, 2016

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.


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>.

const { string } = PropTypes;

class RepoList extends Component {
static propTypes = {
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 remove propTypes. Not essential to this example and introduces class properties which we don't want for now.

@hnordt
Copy link
Contributor

hnordt commented Sep 6, 2016

Please consider https://github.com/smalldots/smalldots#fetch :)

fetch(`https://api.github.com/users/${user}/repos`)
.then((response) => response.json())
.then((repos) => this.setState({ repos }))
.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.

}

componentDidMount() {
this.fetchRepos(this.props.user);
Copy link
Contributor

Choose a reason for hiding this comment

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

If the person copies and pastes this component into App.js, user prop will be undefined. Can you make it so this is actually copy and pasteable with no modifications?

I would suggest that, rather than display repos by user, let's display stargazers of facebook/react repo. Then we can hardcode it.

@gaearon
Copy link
Contributor

gaearon commented Sep 6, 2016

Thank you so much for bearing with me and making these changes. I want to make this really good. 👍

@insin
Copy link
Contributor

insin commented Sep 6, 2016

Should this include examples of displaying a loading indicator and suitable error messages on failure?

e.g. if you're rate-limited - which can happen very quickly with unauthenticated GitHub API calls - response.ok will be false and response.json() will resolve with object with a message property.

@gaearon
Copy link
Contributor

gaearon commented Sep 6, 2016

Yes, this sounds like a great idea. Let's do it.

@rogeliog
Copy link
Contributor Author

Hey! I'm so sorry for the late response, I've been really busy... Sorry once again

@rogeliog
Copy link
Contributor Author

I tried to keep it at simple as possible since I'm a bit worried that the loading and error handling might introduce noise to the example

@rogeliog
Copy link
Contributor Author

rogeliog commented Nov 21, 2016

@gaearon just a friendly ping 😄, I'm more than happy to do any changes needed!

@gaearon
Copy link
Contributor

gaearon commented Feb 11, 2017

Sorry I left this hanging. I was very busy with React in the past few months, and so I left some PRs neglected.

We tried to do something similar in React docs, and it didn’t go well. See the discussion in facebook/react#8098. There are so many possible edge cases and race conditions, all because fetch() API doesn’t support cancellation. (Which, for example, is necessary if props update.)

I’m not sure we should add examples that might set users on the wrong path. At this point I have a lot of doubts about fetch() being a good API for React apps, and I think axios is a better choice.

What do you think?

@hnordt
Copy link
Contributor

hnordt commented Feb 11, 2017

In my experience axios is a better option. Fetch can be considered lower level

@bondz
Copy link
Contributor

bondz commented Feb 13, 2017

CRA does polyfill fetch for apps, most users would just use that anyway without considering the edge cases. Using observables or axios makes cancellation easy, just have to inform users not to use fetch in lifecycle methods.

@Timer Timer mentioned this pull request May 5, 2017
32 tasks
@stereobooster
Copy link
Contributor

Latest specification for fetch adds cancelation. Would be nice to see example of cancel.

@gaearon
Copy link
Contributor

gaearon commented Jan 9, 2018

Thanks again. I'm sorry we didn't reach consensus here and let it slide.
For now I picked some parts from this PR without an extensive example, and merged that as d1adff0.

@gaearon gaearon closed this Jan 9, 2018
@lock lock bot locked and limited conversation to collaborators Jan 20, 2019
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants