React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案

<blockquote>本文是学习了2018年新鲜出炉的React Hooks提案之后,针对<strong>异步请求数据</strong>写的一个案例。注意,本文假设了:<br>1.你已经初步了解<code>hooks</code>的含义了,如果不了解还请移步<a href="https://reactjs.org/docs/hooks-intro.html" rel="nofollow noreferrer">官方文档</a>。(其实有过翻译的想法,不过印记中文一直在翻译,就是比较慢啦)<br>2.你使用<code>Redux</code>实现过异步<code>Action</code>(非必需,只是本文不涉及该部分知识而直接使用)<br>3.你听说过<code>axios</code>或者<code>fetch</code>(如果没有,那么想象一下原生js的<code>promise</code>实现异步请求,或者去学习下这俩库)<br>全部代码参见仓库: <a href="https://github.com/Marckon/React-MPA/tree/master/pages/online-shop" rel="nofollow noreferrer">github | Marckon</a>选择<a href="https://github.com/Marckon/React-MPA/tree/hooks-onlineShop/pages/online-shop" rel="nofollow noreferrer"><code>hooks-onlineShop</code></a>分支以及<code>master</code>分支查看</blockquote> <p>❗ <em><strong>本文并非最佳实践,如有更好的方法或发现文中纰漏,欢迎指正!</strong></em></p> <h1>前序方案(不想看可以直接跳过)</h1> <ul><li><strong>不考虑引入<code>Redux</code></strong></li></ul> <p>通过学习<a href="https://segmentfault.com/a/1190000017175195"><code>React</code>生命周期</a>,我们知道适合进行异步请求的地方是<code>componentDidMount</code>钩子函数内。因此,当你不需要考虑状态管理时,以往的方法很简单:</p>
1class App extends React.Component{ 2 componentDidMount(){ 3 axios.get('/your/api') 4 .then(res=&gt;/*...*/) 5 } 6}
<ul><li><strong>引入<code>Redux</code>进行状态管理</strong></li></ul> <p>当你决定使用<code>Redux</code>进行状态管理时,比如将异步获取到的数据储存在<code>store</code>中,事情就开始复杂起来了。根据<code>Redux</code>的官方文档案例来看,为了实现异步<code>action</code>,你还得需要一个类似于<code>redux-thunk</code>的第三方库来解析你的异步<code>action</code>。</p> <p><em>requestAction.js:</em> 定义异步请求action的地方</p>
1//这是一个异步action,分发了两个同步action,redux-thunk能够理解它 2const fetchGoodsList = url =&gt; dispatch =&gt; { 3 dispatch(requestGoodsList()); 4 axios.get(url) 5 .then(res=&gt;{ 6 dispatch(receiveGoodsList(res.data)) 7 }) 8}; 9
<p><em>requestReducer.js:</em> 处理同步action</p>
1const requestReducer=(state=initialState,action)=&gt;{ 2 switch (action.type) { 3 case REQUEST_GOODSLIST: 4 return Object.assign({},state,{ 5 isFetching: true 6 }); 7 case RECEIVE_GOODSLIST: 8 return Object.assign({},state,{ 9 isFetching:false, 10 goodsList:action.goodsList 11 }); 12 default: 13 return state; 14 } 15};
<p><em>App Component</em> :你引入redux store和redux-thunk中间件的地方</p>
1import {Provider} from 'react-redux'; 2import thunkMiddleWare from 'redux-thunk'; 3import {createStore,applyMiddleware} from 'redux'; 4//other imports 5 6let store=createStore( 7 rootReducer, 8 //这里要使用中间件,才能够完成异步请求 9 applyMiddleware( 10 thunkMiddleWare, 11 myMiddleWare, 12 13 ) 14); 15class App extends React.Component{ 16 render(){ 17 return ( 18 &lt;Provider store={store}&gt; 19 &lt;RootComponent/&gt; 20 &lt;/Provider&gt; 21 ) 22 } 23}
<p><em>GoodsList Component</em> :需要进行异步请求的组件</p>
1class GoodsList extends React.Component{ 2 //... 3 componentDidMount(){ 4 this.props.fetchGoodsList('your/url'); 5 } 6 //... 7} 8const mapDispatchToProps={ 9 fetchGoodsList 10} 11export default connect( 12 mapStateToProps, 13 mapDispatchToProps 14)(GoodsList);
<p>完整代码:<a href="https://github.com/Marckon/React-MPA/tree/master/pages/online-shop" rel="nofollow noreferrer">branch:master-onlineShop</a></p> <h1>使用<code>Hooks</code>-<code>useReducer()</code>和<code>useContext()</code> </h1> <p>总之使用<code>Redux</code>很累,当然,你可以不使用Redux,直接通过<code>props</code>层层传递,或者使用<code>context</code>都可以。只不过本文我们学过了<code>useReducer</code>,使用到了<code>Redux</code>的思想,总要试着用一下。</p> <p><strong>这里你不需要引入别的任何第三方库了,简简单单地使用<code>React@16.7.0-alpha.2</code>版本就好啦</strong></p> <p>很重要的一点就是——<strong>函数式组件</strong>,现在React推荐我们这么做,可以基本上代替<code>class</code>写法。</p> <h2>函数签名</h2> <ol> <li><code>useReducer(reducer,initialState)</code></li> <li><code>useContext(ctxObj)</code></li> <li><code>useEffect(effectFunction,\[dependencyValues\])</code></li> </ol> <h2>概览-你需要编写什么</h2> <ol> <li> <p><strong>action.js:</strong></p> <ul><li>我们还使用<code>redux</code>的思想,编写action</li></ul> </li> <li> <p><strong>reducer.js:</strong></p> <ul><li>处理action,不同于<code>redux</code>的<code>reducer</code>,这里我们可以不用提供初始状态</li></ul> </li> <li> <p><strong>根组件:</strong></p> <ul> <li> <code>Provider</code>提供给子组件<code>context</code> </li> <li> <code>useReducer</code>定义的位置,引入一个<code>reducer</code>并且提供初始状态<code>initialState</code> </li> </ul> </li> <li> <p><strong>子组件:</strong></p> <ul> <li> <code>useContext</code>定义的位置,获取祖先组件提供的<code>context</code> </li> <li> <code>useEffect</code>用于进行异步请求</li> </ul> </li> </ol> <h2>实现</h2> <h3>1.action.js:我们使用action创建函数</h3>
1const REQUEST_GOODSLIST = "REQUEST_GOODSLIST"; 2const RECEIVE_GOODSLIST = "RECEIVE_GOODSLIST"; 3 4//开始请求 5const requestGoodsList = () =&gt; ({ 6 type: REQUEST_GOODSLIST 7}); 8 9//接收到数据 10const receiveGoodsList = json =&gt; ({ 11 type: RECEIVE_GOODSLIST, 12 goodsList: json.goodsList, 13 receivedAt: Date.now() 14}); 15 16export { 17 RECEIVE_GOODSLIST, 18 REQUEST_GOODSLIST, 19 receiveGoodsList, 20 requestGoodsList, 21}
<h3>2.reducer.js:判断action的类型并进行相应处理,更新<code>state</code> </h3>
1import { 2 RECEIVE_GOODSLIST, 3 REQUEST_GOODSLIST, 4} from "../.."; 5 6 7export const fetchReducer=(state,action)=&gt;{ 8 switch (action.type) { 9 case REQUEST_GOODSLIST: 10 return Object.assign({},state,{ 11 isFetching: true 12 }); 13 case RECEIVE_GOODSLIST: 14 return Object.assign({},state,{ 15 isFetching:false, 16 goodsList:state.goodsList.concat(action.goodsList) 17 }); 18 default: 19 return state; 20 } 21}; 22 23
<h3>3.根组件:引入<code>reducer.js</code> </h3>
1import React,{useReducer} from 'react'; 2import {fetchReducer} from '..'; 3 4//创建并export上下文 5export const FetchesContext = React.createContext(null); 6 7function RootComponent() { 8 //第二个参数为state的初始状态 9 const [fetchesState, fetchDispatch] = useReducer(fetchReducer, { 10 isFetching: false, 11 goodsList: [] 12 }); 13 return ( 14 //将dispatch方法和状态都作为context传递给子组件 15 &lt;FetchesContext.Provider value={{fetchesState,dispatch:fetchDispatch}}&gt; 16 //... 17 //用到context的一个子组件 18 &lt;ComponentToUseContext/&gt; 19 &lt;/FetchesContext.Provider&gt; 20 ) 21}
<h3>4.子组件:引入<code>FetchesContext</code> </h3>
1import {FetchesContext} from "../RootComponent"; 2import React, {useContext, useEffect,useState} from 'react'; 3import axios from 'axios'; 4 5function GoodsList() { 6 7 //获取上下文 8 const ctx = useContext(FetchesContext); 9 10 //一个判断是否重新获取的state变量 11 const [reFetch,setReFetch]=useState(false); 12 13 //具有异步调用副作用的useEffect 14 useEffect(() =&gt; { 15 //首先分发一个开始异步获取数据的action 16 ctx.dispatch(requestGoodsList()); 17 axios.get(proxyGoodsListAPI()) 18 .then(res=&gt;{ 19 //获取到数据后分发一个action,通知reducer更新状态 20 ctx.dispatch(receiveGoodsList(res.data)) 21 }) 22 //第二个参数reFetch指的是只有当reFetch变量值改变才重新渲染 23 },[reFetch]); 24 25 return ( 26 &lt;div onScroll={handleScroll}&gt; 27 { 28 //children 29 } 30 &lt;/div&gt; 31 ) 32} 33
<p>完整代码参见:<a href="https://github.com/Marckon/React-MPA/tree/hooks-onlineShop/pages/online-shop" rel="nofollow noreferrer">branch:hooks-onlineShop</a></p> <h3>目录结构</h3> <p>我的目录结构大概这样:</p>
1src 2 |- actions 3 |- fetchAction.js 4 |- components 5 |-... 6 |- reducers 7 |- fetchReducer.js 8 |- index.js
<h2>注意点</h2> <ol> <li>使用<code>useContext()</code>时候我们不需要使用<code>Consumer</code>了。但不要忘记<code>export</code>和<code>import</code>上下文对象</li> <li> <p><code>useEffect()</code>可以看做是<code>class</code>写法的<code>componentDidMount</code>、<code>componentDidUpdate</code>以及<code>componentWillUnMount</code>三个钩子函数的组合。</p> <ul> <li>当返回了一个函数的时候,这个函数就在<code>compnentWillUnMount</code>生命周期调用</li> <li>默认地,传给useEffect的第一个参数会在每次(包含第一次)数据更新时重新调用</li> <li>当给<code>useEffect()</code>传入了第二个参数(数组类型)的时候,effect函数会在第一次渲染时调用,其余仅当数组中的<strong>任一</strong>元素发生改变时才会调用。<strong>这相当于我们控制了组件的<code>update</code>生命周期</strong> </li> <li> <code>useEffect()</code>第二个数组为空则意味着仅在<code>componentDidMount</code>周期执行一次</li> </ul> </li> <li>代码仓库里使用了<code>Mock.js</code>拦截api请求以及<code>ant-design</code>第三UI方库。目前代码比较简陋。</li> </ol> 来源:https://segmentfault.com/a/1190000017209855
点赞
收藏

评论区

加载中...

相关推荐

MySQL:[Err] 1292 - Incorrect datetime value: ‘0000-00-00 00:00:00‘ for column ‘CREATE_TIME‘ at row 1

文章目录问题用navicat导入数据时,报错:原因这是因为当前的MySQL不支持datetime为0的情况。解决修改sql\mode:sql\mode:SQLMode定义了MySQL应支持的SQL语法、数据校验等,这样可以更容易地在不同的环境中使用MySQL。全局s

Oracle 分组与拼接字符串同时使用

SELECTT.,ROWNUMIDFROM(SELECTT.EMPLID,T.NAME,T.BU,T.REALDEPART,T.FORMATDATE,SUM(T.S0)S0,MAX(UPDATETIME)CREATETIME,LISTAGG(TOCHAR(

MySQL部分从库上面因为大量的临时表tmp_table造成慢查询

背景描述Time:20190124T00:08:14.70572408:00User@Host:@Id:Schema:sentrymetaLast_errno:0Killed:0Query_time:0.315758Lock_

皕杰报表之UUID

​在我们用皕杰报表工具设计填报报表时,如何在新增行里自动增加id呢?能新增整数排序id吗?目前可以在新增行里自动增加id,但只能用uuid函数增加UUID编码,不能新增整数排序id。uuid函数说明:获取一个UUID,可以在填报表中用来创建数据ID语法:uuid()或uuid(sep)参数说明:sep布尔值,生成的uuid中是否包含分隔符'',缺省为

手写Java HashMap源码

HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程HashMap的使用教程22

java将前端的json数组字符串转换为列表

记录下在前端通过ajax提交了一个json数组的字符串,在后端如何转换为列表。前端数据转化与请求varcontracts{id:'1',name:'yanggb合同1'},{id:'2',name:'yanggb合同2'},{id:'3',name:'yang