本文是学习了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方库。目前代码比较简陋。