-
Notifications
You must be signed in to change notification settings - Fork 47k
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
[docs] Loading Data Asynchronously #8883
Changes from all commits
b06e0d5
382fee4
b545300
34aedac
4c92f0e
55ead66
4b1fa64
461082c
763c790
c1a36a1
72632e9
a9a6007
74cc978
1c4c1c9
4e0f3ce
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
--- | ||
id: loading-data-asynchronously | ||
title: Loading Data Asynchronously | ||
permalink: docs/loading-data-asynchronously.html | ||
--- | ||
|
||
React has no special capabilities for dealing with asynchronous network requests and a third party library or browser API is needed to perform them. If a component needs to have its UI respond to new data arriving, it has to call `setState` to rerender itself. | ||
|
||
## Initial Render | ||
|
||
Often, the data that a component needs is not available at initial render. We can load data asynchronously in the [`componentDidMount` lifecycle hook](/react/docs/react-component.html#componentdidmount). | ||
|
||
In the following example we use the [axios](https://github.com/mzabriskie/axios) to retrieve information about Facebook's Repos on GitHub and store them in the state. So first install it: | ||
|
||
``` | ||
$ npm install axios --save | ||
``` | ||
|
||
And you can use it in your project: | ||
|
||
```javascript{1,9-13} | ||
import axios from 'axios'; | ||
|
||
class Repos extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = {repos: []}; | ||
} | ||
|
||
componentDidMount() { | ||
axios.get('https://api.github.com/users/facebook/repos') | ||
.then(response => this.setState({ repos: response.data })) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens here if the component has unmounted before this request completes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I suppose we need to add cancel to componentWillUnmount. componentDidMount() {
let CancelToken = axios.CancelToken;
let cancel
let cancelToken = CancelToken(c => cancel = c)
this.setState({ cancel: cancel })
axios.get('https://api.github.com/users/facebook/repos', { cancelToken })
.then(response => this.setState({ repos: response.data }))
.catch(error => console.log(error));
}
componentWillUnmount() {
this.state.cancel()
} UPD there is example with cancelation lower |
||
.catch(error => console.log(error)); | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<h1>Repos by facebook</h1> | ||
{this.state.repos.map(repo => | ||
<div key={repo.id}>{repo.name}</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
} | ||
``` | ||
|
||
The component will perform an initial render without any of the network data. When the axios promise resolves, it calls `setState` and the component is rerendered. | ||
|
||
[Try it on CodePen.](http://codepen.io/dashtinejad/pen/wgzEXJ?editors=0011) | ||
|
||
## Updates | ||
|
||
If the props change, we might need to fetch new data for the updated props. The [`componentDidUpdate` lifecycle hook](/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 repos when it changes: | ||
|
||
```javascript{7-11,,13-15,17-21,35,38,44-47,53} | ||
class Repos extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = {repos: []}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what about loading state e.g. spinner? |
||
} | ||
|
||
fetchRepos() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is this fetchRepos function supposed to have a particular behavior in terms of doing things in the right order? Does axios guarantee that? |
||
axios.get(`https://api.github.com/users/${this.props.username}/repos`) | ||
.then(response => this.setState({ repos: response.data })) | ||
.catch(error => console.log(error)); | ||
} | ||
|
||
componentDidMount() { | ||
this.fetchRepos(); | ||
} | ||
|
||
componentDidUpdate(prevProps) { | ||
if (this.props.username != prevProps.username) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Won't this lead to, if you first load repos for foo, and then for bar, on the immediate load of bar it will show the foo repos under a header that says "bar repo"? |
||
this.fetchRepos(); | ||
} | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<h1>Repos by {this.props.username}</h1> | ||
{this.state.repos.map(repo => | ||
<div key={repo.id}>{repo.name}</div> | ||
)} | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
class App extends React.Component { | ||
constructor(props) { | ||
super(props); | ||
this.state = {username: 'facebook'}; | ||
} | ||
|
||
render() { | ||
return ( | ||
<div> | ||
<button onClick={() => this.setState({username: 'facebook'})}>Facebook</button> | ||
<button onClick={() => this.setState({username: 'microsoft'})}>Microsoft</button> | ||
<button onClick={() => this.setState({username: 'google'})}>Google</button> | ||
<Repos username={this.state.username} /> | ||
</div> | ||
); | ||
} | ||
} | ||
|
||
ReactDOM.render(<App />, document.getElementById('app')) | ||
``` | ||
|
||
[Try it on CodePen.](http://codepen.io/dashtinejad/pen/zNpzVW?editors=0011) | ||
|
||
## Cancellation | ||
|
||
An old promise can be pending when a newer promise fulfills. This can cause the old promise to override the result of the new one. If a promise is pending when a component is updated, the result of the first promise should be ignored before a new one is created. | ||
Some data fetching APIs allow you to cancel requests, and for axios, we use [Cancellation](https://github.com/mzabriskie/axios#cancellation) by token: | ||
|
||
```javascript{5-8,10-11,14-15,19-23} | ||
class Repos extends React.Component { | ||
// removed for brevity | ||
|
||
fetchRepos() { | ||
// cancel the previous request | ||
if (typeof this._source != typeof undefined) { | ||
this._source.cancel('Operation canceled due to new request.') | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is it possible that |
||
} | ||
|
||
// save the new request for cancellation | ||
this._source = axios.CancelToken.source(); | ||
|
||
axios.get(`https://api.github.com/users/${this.props.username}/repos`, | ||
// cancel token used by axios | ||
{ cancelToken: this._source.token } | ||
) | ||
.then(response => this.setState({ repos: response.data })) | ||
.catch(error => { | ||
if (axios.isCancel(error)) { | ||
console.log('Request canceled', error); | ||
} else { | ||
console.log(error); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. state error - show error message. Not that much code, but will encourage programmers to implement error messages and think of all states of component with async load |
||
} | ||
}); | ||
} | ||
|
||
// removed for brevity | ||
} | ||
``` | ||
|
||
[Try it on CodePen.](http://codepen.io/dashtinejad/pen/Lxejpq?editors=0011) | ||
|
||
[You can clone the whole sourcecode from GitHub](https://github.com/dashtinejad/react-ajax-axios). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you say "repositories" instead of "Repos"? I think a lot of people will not instinctively know what a "repo" is.