本文所使用的React
爲最新版16.83,其中用到了高階組件react.memo()
,處理異步請求的 async/await,基於Promise 的 HTTP 庫axios
用於數據請求,以及用來取消請求的 axios APIaxios.CancelToken.source()
,用心看下去你會有所收穫。
在文中,咱們將使用 React 結合 Axios 在應用中構建實時搜索功能。會對沒必要要的請求進行處理優化,而且還對HTTP請求數據進行緩存等。java
本文中,咱們使用 react 官方提供的 Create React App 來初始化應用程序,須要 node >= 6.0,npm >= 5.2 。而後運行如下命名:node
npx create-react-app axios-react cd axios-react npm start or yarn start
應用程序初始化後,安裝 axios:react
npm install axios or yarn add axios
接下來,將下面的代碼複製到 App.js
組件中,咱們先不用管 Movies
組件裏是什麼。ios
import React, { Component } from 'react'; import axios from 'axios'; import Movies from './Movies; class App extends Component { //state 定義咱們所須要的數據 state = { value: '', movies: null, loading: false } search = async val => { this.setState({ loading: true }); const res = await axios( `https://api.themoviedb.org/3/search/moviequery=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b` ); const movies = await res.data.results; this.setState({ movies, loading: false }); }; onChangeHandler = async e => { this.search(e.target.value); this.setState({ value: e.target.value }) } get renderMovies() { let movies = <h1>沒有搜索結果</h1> if(this.state.loading) { movies = <h1>LOADING ...</h1> } if(this.state.movies) { movies = <Movies list={this.state.movies}></Movies>; } return movies; } render() { return ( <div> <input className="Search" value={this.state.value} onChange={e => this.onChangeHandler(e)} placeholder="試着輸入一些內容" /> <div className="ContainerInner"> {this.renderMovies} </div> </div> ); } } export default App;
從代碼中咱們看到有一個受控 input
元素,當輸入內容時,調用onChangeHandler
方法。onChangeHandler
方法作了兩件事情:一是調用 search 方法並將輸入的內容傳參給 search
;二是修改 state 中的 value屬性。web
onChangeHandler = async e => { //調用search 方法 this.search(e.target.value); //更改state this.setState({ value: e.target.value }) }
在 search
方法中,咱們使用 GET 請求,從API獲取咱們想要的數據。一旦請求發送成功時,咱們就會更新組件 state
的 loading 屬性爲 true,表示正在等待結果。當咱們獲得返回的結果時,將更新 state movies 屬性,並關閉請求狀態。咱們使用了 async/await
來處理異步請求,這裏先不對這個作過多的介紹。npm
search = async val => { this.setState({ loading: true }); const res = await axios( `https://api.themoviedb.org/3/search/moviequery=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b` ); const movies = await res.data.results; this.setState({ movies, loading: false }); };
在這裏咱們使用名爲 renderMovies
的 get 方法經過 props
將獲取到的數據傳遞給 Movies
組件。axios
get renderMovies() { let movies = <h1>沒有搜索結果</h1> if(this.state.loading) { movies = <h1>LOADING ...</h1> } if(this.state.movies) { movies = <Movies list={this.state.movies}></Movies>; } return movies; }
咱們在src 目錄下建立 Movies.js
文件,並複製下邊代碼到此文件中。api
import React from 'react'; const Movie = props => { let movie = <h1>暫無數據</h1>; if(props.list.length) { movie = props.list.map((item, index) => { const { title, poster_path, vote_average } = item; const src = poster_path && `url(http://image.tmdb.org/t/p/w185${poster_path})`; return ( <div className="Container" style={{ backgroundImage: src }} key={index}> <div className="VoteContainer"> <span className="Vote">{vote_average}</span> </div> <div className="Bottom"> <h3 className="Title">{title}</h3> </div> </div> ) }) } return movie; } //16.6版本中更新了一些包裝函數 //其中 React.memo() 是一個高階函數 //它與 React.PureComponent相似 //可是一個純函數組件而非一個類 const Movies = React.memo(Movie); export default Movies;
Movies 組件作的事情很簡單,就是將從 props 裏接收過來的數據進行解析,並渲染到頁面上,這裏用到了包裝函數 (wrapped functions) Reacta.memo()
用來優化應用性能,這裏不對這個函數進行過多的介紹。數組
就是這麼簡單?緩存
防止沒必要要的請求
咱們打開瀏覽中的開發者工具 network 選項,您可能會注意到咱們每次更新輸入時都會發送請求,而且會有大量的重複請求,這可能致使請求過載,尤爲是當咱們收到大量響應時。
在解決這個問題以前,咱們先在 src 目錄下建立 utils.js
文件,將如下代碼複製進去。
import axios from 'axios'; const axiosRequester = () => { let cancel; return async url => { if(cancel) { //若是token存在,就取消請求 cancel.cancel(); } //建立一個新的cancelToken cancel = axios.CancelToken.source(); try { const res = await axios(url, { cancelToken: cancel.token }) const result = res.data.results; return result; } catch(error) { if(axios.isCancel(error)) { console.log('Request canceled', error.message); } else { console.log(error.message); } } } } export const _search = axiosRequester();
修改 App
組件中的代碼:
... import { _search } from './utils'; class App extends Component { ... search = async val => { this.setState({ loading: true } const url = `https://api.themoviedb.org/3/search/movie?query=${val}&api_key=dbc0a6d62448554c27b6167ef7dabb1b` const res = await _search(url); const movies = res; this.setState({ movies, loading: false }); } ...
Axios
提供了所謂的取消令牌(cancel token)功能,容許咱們取消請求。
在 axiosRequester
咱們建立一個名爲的 cancel 變量。而後發送請求,若是 cancel 變量存在,咱們調用其 cancel 方法取消先前的請求。而後咱們分配 一個 新的 token
給 CancelToken
。以後,咱們使用給定的查詢發出請求並返回結果。
咱們使用 try/catch
對問題進行捕獲,咱們能夠檢查並處理請求是否被取消。
咱們如今看看開發者工具中 network 是什麼樣子的:
咱們看到請求都被咱們取消掉了,由於這些請求以前已經請求過了。
若是咱們在屢次輸入中鍵入相同的文本,咱們每次都會發出一次新的請求,這顯然不是咱們想要的結果。解決這個問題,咱們將 utils.js
稍微改變下:
import axios from 'axios'; //用來存儲已經請求過的數據 const resources = {}; const axiosRequester = () => { let cancel; return async url => { if(cancel) { //若是token存在,就取消請求 cancel.cancel(); } //建立一個新的cancelToken cancel = axios.CancelToken.source(); try { //若是請求的數據已經存在,緩存以前請求回來的數據 if(resources[url]) { return resources[url]; } const res = await axios.post(url, { cancelToken: cancel.token }) const result = res.data.results; //將url做爲key, 記錄請求回來的數據 resources[url] = result; return result; } catch(error) { if(axios.isCancel(error)) { console.log('Request canceled', error.message); } else { console.log(error.message); } } } } export const _search = axiosRequester();
這裏咱們建立了一個 resources
對象用來緩存咱們請求的結果。當正在執行新請求時,咱們首先檢查咱們的 resources
對象是否具備這次查詢的結果。若是存在,咱們只返回該結果。若是不存在,咱們會發出新請求並將對應的結果存儲到 resources
中。
讓咱們用幾句話總結一下,當咱們在input框中輸入內容時:
若是有過的話,咱們取消以前的請求。若是咱們已經輸入了以前的內容,咱們只需返回以前的數據,不會發出新請求。
若是是新的請求,咱們會將得到的新的數據緩存起來。
若是想要了解更多,請搜索微信公衆號:webinfoq
。