Skip to content
3 changes: 2 additions & 1 deletion docs/_data/nav_docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@
title: JSX In Depth
- id: typechecking-with-proptypes
title: Typechecking With PropTypes
- id: loading-data-asynchronously
title: Loading Data Asynchronously
- id: refs-and-the-dom
Copy link
Collaborator

Choose a reason for hiding this comment

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

If we put it here, "refs and DOM" will need its prev link changed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, I realized that after I made the PR. Didn't want to change it in case this guide should be elsewhere. Should we keep it here then?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Going back to it, I saw that other guides in the same section don't have previous links. I removed the one in this article to keep consistency.

title: Refs and the DOM
- id: optimizing-performance
Expand Down Expand Up @@ -80,4 +82,3 @@
title: Shallow Compare
- id: two-way-binding-helpers
title: Two-way Binding Helpers

114 changes: 114 additions & 0 deletions docs/docs/loading-data-asynchronously.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
---
id: loading-data-asynchronously
title: Loading Data Asynchronously
permalink: docs/loading-data-asynchronously.html
prev: typechecking-with-proptypes.html
---


Copy link
Collaborator

Choose a reason for hiding this comment

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

We need to explain the key concept somewhere early. We need to hammer home the idea that in React, any update means a change in the state. It doesn't have any special capabilities for handling AJAX: if you want to change the UI as a result of data arriving, you need to change the state.

Often, the data that a component needs is not available at initial render. We can load data asynchronously in the `componentDidMount` [lifecycle hook](https://facebook.github.io/react/docs/react-component.html#componentdidmount).
Copy link
Collaborator

Choose a reason for hiding this comment

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

Please leave the domain out of the links. Keep it as /react/docs/react-component.html#componentdidmount.


In the following example we use the `fetch` API to retrieve information about Facebook's Gists on GitHub and store them in the state.
Copy link
Collaborator

Choose a reason for hiding this comment

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

fetch should link to MDN.

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should mention that this is a browser API but it requires a polyfill. Link to the polyfill. Mention that if you use Create React App, polyfill is already included.


```javascript{10-14}
class Gists extends React.Component {
constructor(props) {
super(props);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: let's write it shorter,

constructor(props) {
  super(props);
  this.state = {gists: []};
}

this.state = {
gists: [],
};
}
componentDidMount() {
fetch('https://api.github.com/users/facebook/gists')
Copy link
Contributor

Choose a reason for hiding this comment

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

I think this code is not using fetch appropriately - you should be checking response.ok to see if there is an error. As is, if there is a 4xx or 5xx error I think it will fail in some weird way during parsing. Even just rejecting a promise would be OK here IMO but fetch actually does not reject the promise on server failure.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah sorry, my bad.

.then(res => res.json())
.then(gists => this.setState({ gists }));
}
render() {
const { gists } = this.state;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: let's not put a newline here.

return (
<div>
<h1>Gists by facebook</h1>
{gists.map(gist => <p><a href={gist.html_url}>{gist.id}</a></p>)}
Copy link
Collaborator

Choose a reason for hiding this comment

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

Nit: let's put the JSX on next line.

{gists.map(gist =>
  <p>...</p>
)}

</div>
);
}
}
```

Note that, the component will perform an initial render without any of the network data. When the fetch promise resolves, it calls `setState` and the component is rerendered.

## Updates

If the props change, we might need to fetch new data for the updated props. The `componentDidUpdate` [lifecycle hook](https://facebook.github.io/react/docs/react-component.html#componentdidupdate) is a good place to achieve this, since we may not need to fetch new data if the props that we're interested in have not changed.

Building on the previous example, we will pass the username as a prop instead and fetch new gists when it changes:

```javascript{10-20}
class Gists extends React.Component {
componentDidMount() {
const { username } = this.props;
fetch(`https://api.github.com/users/${username}/gists`)
.then(res => res.json())
.then(gists => this.setState({ gists }));
}
componentDidUpdate(prevProps) {
const { username } = this.props;
// Make sure that the `username` prop did change before
// we initiate a network request.
if (username !== prevProps.username) {
fetch(`https://api.github.com/users/${username}/gists`)
.then(res => res.json())
.then(gists => this.setState({ gists }));
}
}
render() {
const { username } = this.props;
const { gists } = this.state;
return (
<div>
<h1>Gists by {username}.</h1>
{gists.map(gist => <p><a href={gist.html_url}>{gist.id}</a></p>)}
</div>
);
}
/* ... */
Copy link
Collaborator

Choose a reason for hiding this comment

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

What's here? I'd rather avoid ambiguous "old stuff" blocks.

Copy link
Contributor Author

@rthor rthor Oct 27, 2016

Choose a reason for hiding this comment

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

Just the constructor. Should I repeat it instead? Wasn't sure what your preference would be.

}
```

We can extract the common code in `componentDidMount` and `componentDidUpdate` into a new method, `fetchGists`, and call that in both lifecycle hooks.
Copy link

Choose a reason for hiding this comment

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

I think we should get rid of this refactor step. It adds 40 lines just to explain to the reader that if they write the same code twice, they should probably turn it into a function. The other option is for the first example to already have the fetchGists method.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

While I agree that this adds a lot to the guide, I think that this way is more in tone with the rest of the documentation. Ie show a "simplified" version first and gradually refactor towards best practices.


```javascript
class Gists extends React.Component {
componentDidMount() {
this.fetchGists();
}

componentDidUpdate(prevProps) {
if (this.props.username !== prevProps.username) {
this.fetchGists();
}
}

fetchGists() {
const { username } = this.props;

fetch(`https://api.github.com/users/${username}/gists`)
.then(res => res.json())
.then(gists => this.setState({ gists }));
}

/* ... */
}
```

Copy link
Collaborator

Choose a reason for hiding this comment

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

We should have the last section that shows the same with async/await I think. Async/await is enabled in Create React App by default.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

In that case, shouldn't all the examples feature async / await? Or is it better to demonstrate both methods?

[Try it out on CodePen.](http://codepen.io/rthor/pen/kkqrQx?editors=0010)