若是你曾經使用過相似 Angular
框架的經驗,你會發現這些框架集成了開發一個應用應該具有的全部功能,例如用於進行HTTP調用的服務(Angular
中的 $HTTP
)。javascript
React
是一個視圖層框架,用於構建用戶界面。在 MVC
架構中,它僅僅負責視圖部分。在實際的開發過程當中,每每須要咱們引入一些第三方組件,好比今天要說的 AJAX API
。css
本文經過一個簡單的示例,學習經過不一樣的方式(如 Axios
、 XMLHttpRequest
或 fetch API
)使用來進行 AJAX
請求(GET、POST、PUT和DELETE),以獲取、建立、更新和刪除數據。java
有不少的方案, 你所作的就是選擇合適的解決方案:node
一、對JavaScript
中 Promise
比較熟悉,可使用 Axios。react
二、喜歡使用前沿標準,可使用 fetch API
。ios
三、也可使用 jQuery
,但不建議僅僅爲了進行API調用而引入整個庫。git
能夠直接使用 XMLHttpRequest 接口。github
我推薦使用 create-react-app
來建立應用,由於它省去了好多的配置。ajax
npm install -g create-react-app
複製代碼
經過執行如下命令建立並啓動咱們的應用:react-ajax-demo
npm
create-react-app react-ajax-demo
cd react-ajax-demo
npm start
複製代碼
下面引自Mozilla MDN:
Fetch API 提供了一個獲取資源的接口(包括跨域)。任何使用過 XMLHttpRequest 的人都能輕鬆上手,但新的API提供了更強大和靈活的功能集。
讀取Reddit上發佈的一些帖子,src/App.js
:
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
fetchFirst(url) {
var that = this;
if (url) {
fetch('https://www.reddit.com/r/' + url + '.json').then(function (response) {
return response.json();
}).then(function (result) {
that.setState({ posts: result.data.children, lastPostName: result.data.children[result.data.children.length - 1].data.name });
console.log(that.state.posts);
});
}
}
componentWillMount() {
this.fetchFirst("reactjs");
}
render() {
return (
<div className="App"> <header className="App-header"> <img src={logo} className="App-logo" alt="logo" /> <h1 className="App-title">React AJAX Example</h1> </header> <p className="App-intro"> <ul> {this.state.posts.map(post => <li key={post.id}>{post.title}</li> )} </ul> </p> </div> ); } } export default App; 複製代碼
在 fetchFirst()
方法中,咱們發起了一個GET請求:
fetch('https://www.reddit.com/r/' + url + '.json').then(function (response) {
return response.json();
}).then(function (result) {
console.log(result.data.children);
});
複製代碼
API很簡單,須要的惟一參數就是資源的URI。這裏它是一個JSON文件,也能夠是任何類型的資源,如圖像或其餘類型。
**fetch(url)**在默認狀況下發送GET HTTP請求。
還能夠經過第二個參數中指定方法名來調用其餘HTTP方法,如POST、PUT或DELETE。
例如,使用 fetch()
發送POST請求:
var form = new FormData(document.getElementById('login-form'));
fetch("/login", {
method: "POST",
body: form
});
複製代碼
一個典型的 React
應用是一系列包含根組件、父組件以及子組件(以及子組件的子組件)的組件,這些組件能夠可看做爲一個樹狀結構。
這裏會產生許多問題: 一、如何處理數據? 二、數據如何從組件流到其餘組件? 三、最重要的是,在哪裏放異步請求的代碼?
componentWillMount()
是React生命週期的一個重要事件,執行時機在組件第一次渲染以前,在組件即將掛載時調用該事件。
前面的例子中,咱們就是在 componentWillMount()
事件內部發起 AJAX
請求。
其實還有幾個地方能夠這麼作:
一、componentDidMount()
: 這是官方推薦的地方
二、componentWillMount()
: 官方不推薦
三、constructor()
:它被認爲是反模式! 可是能夠像 componentWillMount()
同樣使用。
componentWillMount()
方法在組件的第一次渲染以前被調用。任何人誤覺得這是獲取數據的最佳場所。但事實並不是如此!由於 fetch
調用是異步的,這意味着在組件渲染以前異步請求有可能還沒返回。通常採起的作法是:在 constructor()
設置初始狀態,爲了 React
能正確地渲染組件,在異步請求返回後觸發組件的從新渲染。
componentDidMount()
方法是在組件第一次渲染以後調用的,所以能夠放心在這裏執行任何異步代碼,這些操做能夠是獲取服務器數據,也能夠是操做DOM。
在實現服務端渲染時,componentWillMount()會調用兩次,所以會有兩次請求發送給服務器,這是一種浪費。而componentDidMount()只會在客戶端調用一次。
咱們對上面的示例作下改動:
componentDidMount() {
this.fetchFirst("reactjs");
}
複製代碼
從程序的角度分析,是否能夠用 constructor
代替 componentWillMount()
?
表面上看確實是能夠替代的,但這並非一種正確的方式。
由於咱們若是學過純函數的話,都知道其實異步請求是一種典型的有反作用的非純函數。若是組件構造函數中存非純函數,React
會拋出一個警告, 而 componentWillMount()
中是容許非純函數的。若是代碼中須要更新其餘組件中的狀態,最好不要使用構造函數。
最後一個結論是:對於任何不產生反作用(如狀態更新)的初始化代碼使用構造函數,不然使用componentDidMount()。
另外一種方法是從父組件向子組件傳遞數據。固然,父組件也應該避免以前獲取數據的方法,那麼在哪裏呢?父節點可使用React路由器鉤子(如 onEnter()
)獲取數據,而後經過 props
將數據傳遞給子節點。
在本文示例中,因爲只有一個組件,但這能夠很容易地分解爲如下內容:
Fetch.js
export default {
fetchFirst: function(url){
fetch('https://www.reddit.com/r/' + url + '.json').then(function (response) {
return response.json();
}).then(function (result) {
return result.data.children;
});
}
}
複製代碼
RedditPost.js
import React from "react";
class RedditPost extends React.Component {
constructor(props) {
super(props);
this.state = {
title: props.title,
id: props.id
}
}
componentWillReceiveProps(nextProps) {
this.setState(nextProps);
}
render() {
<li key={this.state.id}>{this.state.title}</li>
);
}
}
}
RedditPost.propTypes = {
id: React.PropTypes.number,
title: React.PropTypes.string
};
RedditPost.defaultProps = {
id: null,
title: ""
};
export default RedditPost;
複製代碼
App.js
import fetchFirst from "./Fetch";
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
class App extends Component {
constructor(props) {
super(props);
this.state = {
posts: []
};
}
componentDidMount() {
fetchFirst("reactjs").then((posts)=>{
this.state.posts = posts;
});
}
render() {
return (
<div className="App">
<header className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h1 className="App-title">React AJAX Example</h1>
</header>
<p className="App-intro">
<ul>
{this.state.posts.map(post =>
<RedditPost {...post}/>
)}
</ul>
</p>
</div>
);
}
}
export default App;
複製代碼
第三種方法是使用 Redux
這樣的狀態管理器。
從代碼中的任何地方獲取數據,而後將數據存儲在全局存儲中,全部其餘組件均可以使用這些數據。
關於對 Redux
的學習,你能夠自行了解,也能夠讀一下我以前寫的幾篇文章: 一、Redux-如何更優雅的修改全局狀態 二、從新思考Redux
愈來愈多的企業在構建本身的網站時開始注重體驗、性能,出現了大量的單頁面應用,將 AJAX
這種無刷技術發揮到了極致。而咱們在使用 React
開發應用時能夠有多種 AJAX
方案:
一、使用XMLHttpRequest
,若是以爲複雜,可直接使用 jQuery
。 二、Fetch API
, 現代瀏覽器都支持,老版瀏覽器須要使用 polyfills
。 三、Axios
:一個基於 promise
的 HTTP
庫, 能夠用在瀏覽器和 node.js
中。