本文是學習了2018年新鮮出爐的React Hooks提案以後,針對 異步請求數據寫的一個案例。注意,本文假設了:
1.你已經初步瞭解hooks
的含義了,若是不瞭解還請移步 官方文檔。(其實有過翻譯的想法,不過印記中文一直在翻譯,就是比較慢啦)
2.你使用Redux
實現過異步Action
(非必需,只是本文不涉及該部分知識而直接使用)
3.你據說過axios
或者fetch
(若是沒有,那麼想象一下原生js的promise
實現異步請求,或者去學習下這倆庫)
所有代碼參見倉庫: github | Marckon選擇hooks-onlineShop
分支以及master
分支查看
❗ 本文並不是最佳實踐,若有更好的方法或發現文中紕漏,歡迎指正!javascript
Redux
經過學習React
生命週期,咱們知道適合進行異步請求的地方是componentDidMount
鉤子函數內。所以,當你不須要考慮狀態管理時,以往的方法很簡單:html
class App extends React.Component{ componentDidMount(){ axios.get('/your/api') .then(res=>/*...*/) } }
Redux
進行狀態管理當你決定使用Redux
進行狀態管理時,好比將異步獲取到的數據儲存在store
中,事情就開始複雜起來了。根據Redux
的官方文檔案例來看,爲了實現異步action
,你還得須要一個相似於redux-thunk
的第三方庫來解析你的異步action
。java
requestAction.js: 定義異步請求action的地方react
//這是一個異步action,分發了兩個同步action,redux-thunk可以理解它 const fetchGoodsList = url => dispatch => { dispatch(requestGoodsList()); axios.get(url) .then(res=>{ dispatch(receiveGoodsList(res.data)) }) };
requestReducer.js: 處理同步actionios
const requestReducer=(state=initialState,action)=>{ switch (action.type) { case REQUEST_GOODSLIST: return Object.assign({},state,{ isFetching: true }); case RECEIVE_GOODSLIST: return Object.assign({},state,{ isFetching:false, goodsList:action.goodsList }); default: return state; } };
App Component :你引入redux store和redux-thunk中間件的地方git
import {Provider} from 'react-redux'; import thunkMiddleWare from 'redux-thunk'; import {createStore,applyMiddleware} from 'redux'; //other imports let store=createStore( rootReducer, //這裏要使用中間件,纔可以完成異步請求 applyMiddleware( thunkMiddleWare, myMiddleWare, ) ); class App extends React.Component{ render(){ return ( <Provider store={store}> <RootComponent/> </Provider> ) } }
GoodsList Component :須要進行異步請求的組件github
class GoodsList extends React.Component{ //... componentDidMount(){ this.props.fetchGoodsList('your/url'); } //... } const mapDispatchToProps={ fetchGoodsList } export default connect( mapStateToProps, mapDispatchToProps )(GoodsList);
完整代碼:branch:master-onlineShopjson
Hooks
-useReducer()
和useContext()
總之使用Redux
很累,固然,你能夠不使用Redux,直接經過props
層層傳遞,或者使用context
均可以。只不過本文咱們學過了useReducer
,使用到了Redux
的思想,總要試着用一下。redux
這裏你不須要引入別的任何第三方庫了,簡簡單單地使用React@16.7.0-alpha.2
版本就好啦axios
很重要的一點就是——函數式組件,如今React推薦咱們這麼作,能夠基本上代替class
寫法。
useReducer(reducer,initialState)
useContext(ctxObj)
useEffect(effectFunction,[dependencyValues])
action.js:
redux
的思想,編寫actionreducer.js:
redux
的reducer
,這裏咱們能夠不用提供初始狀態根組件:
Provider
提供給子組件context
useReducer
定義的位置,引入一個reducer
而且提供初始狀態initialState
子組件:
useContext
定義的位置,獲取祖先組件提供的context
useEffect
用於進行異步請求const REQUEST_GOODSLIST = "REQUEST_GOODSLIST"; const RECEIVE_GOODSLIST = "RECEIVE_GOODSLIST"; //開始請求 const requestGoodsList = () => ({ type: REQUEST_GOODSLIST }); //接收到數據 const receiveGoodsList = json => ({ type: RECEIVE_GOODSLIST, goodsList: json.goodsList, receivedAt: Date.now() }); export { RECEIVE_GOODSLIST, REQUEST_GOODSLIST, receiveGoodsList, requestGoodsList, }
state
import { RECEIVE_GOODSLIST, REQUEST_GOODSLIST, } from "../.."; export const fetchReducer=(state,action)=>{ switch (action.type) { case REQUEST_GOODSLIST: return Object.assign({},state,{ isFetching: true }); case RECEIVE_GOODSLIST: return Object.assign({},state,{ isFetching:false, goodsList:state.goodsList.concat(action.goodsList) }); default: return state; } };
reducer.js
import React,{useReducer} from 'react'; import {fetchReducer} from '..'; //建立並export上下文 export const FetchesContext = React.createContext(null); function RootComponent() { //第二個參數爲state的初始狀態 const [fetchesState, fetchDispatch] = useReducer(fetchReducer, { isFetching: false, goodsList: [] }); return ( //將dispatch方法和狀態都做爲context傳遞給子組件 <FetchesContext.Provider value={{fetchesState,dispatch:fetchDispatch}}> //... //用到context的一個子組件 <ComponentToUseContext/> </FetchesContext.Provider> ) }
FetchesContext
import {FetchesContext} from "../RootComponent"; import React, {useContext, useEffect,useState} from 'react'; import axios from 'axios'; function GoodsList() { //獲取上下文 const ctx = useContext(FetchesContext); //一個判斷是否從新獲取的state變量 const [reFetch,setReFetch]=useState(false); //具備異步調用反作用的useEffect useEffect(() => { //首先分發一個開始異步獲取數據的action ctx.dispatch(requestGoodsList()); axios.get(proxyGoodsListAPI()) .then(res=>{ //獲取到數據後分發一個action,通知reducer更新狀態 ctx.dispatch(receiveGoodsList(res.data)) }) //第二個參數reFetch指的是隻有當reFetch變量值改變才從新渲染 },[reFetch]); return ( <div onScroll={handleScroll}> { //children } </div> ) }
完整代碼參見:branch:hooks-onlineShop
個人目錄結構大概這樣:
src |- actions |- fetchAction.js |- components |-... |- reducers |- fetchReducer.js |- index.js
useContext()
時候咱們不須要使用Consumer
了。但不要忘記export
和import
上下文對象useEffect()
能夠看作是class
寫法的componentDidMount
、componentDidUpdate
以及componentWillUnMount
三個鉤子函數的組合。
compnentWillUnMount
生命週期調用useEffect()
傳入了第二個參數(數組類型)的時候,effect函數會在第一次渲染時調用,其他僅當數組中的任一元素髮生改變時纔會調用。這至關於咱們控制了組件的update
生命週期 useEffect()
第二個數組爲空則意味着僅在componentDidMount
週期執行一次Mock.js
攔截api請求以及ant-design
第三UI方庫。目前代碼比較簡陋。