Open
Description
此本转载至Marckon的文章:
【React Hooks实现异步请求实例—useReducer、useContext和useEffect代替Redux方案】
原文地址;
使用Hooks
-useReducer()
和useContext()
总之使用Redux
很累,当然,你可以不使用Redux,直接通过props
层层传递,或者使用context
都可以。只不过本文我们学过了useReducer
,使用到了Redux
的思想,总要试着用一下。
这里你不需要引入别的任何第三方库了,简简单单地使用React@16.8版本就好啦
很重要的一点就是——函数式组件,现在React推荐我们这么做,可以基本上代替class
写法。
函数签名
-
useReducer(reducer,initialState)
-
useContext(ctxObj)
-
useEffect(effectFunction,[dependencyValues])
概览-你需要编写什么
- action.js:
- 我们还使用
redux
的思想,编写action
- 我们还使用
- reducer.js:
- 处理action,不同于
redux
的reducer
,这里我们可以不用提供初始状态
- 处理action,不同于
- 根组件:
Provider
提供给子组件context
useReducer
定义的位置,引入一个reducer
并且提供初始状态initialState
- 子组件:
useContext
定义的位置,获取祖先组件提供的context
useEffect
用于进行异步请求
实现
1.action.js:我们使用action创建函数
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,
}
2.reducer.js:判断action的类型并进行相应处理,更新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;
}
};
3.根组件:引入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>
)
}
4.子组件:引入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>
)
}
我的目录结构大概这样:
src
|- actions
|- fetchAction.js
|- components
|-...
|- reducers
|- fetchReducer.js
|- index.js
完整代码参见:branch:hooks-onlineShop;
注意点
- 使用
useContext()
时候我们不需要使用Consumer
了。但不要忘记export
和import
上下文对象 useEffect()
可以看做是class
写法的componentDidMount
、componentDidUpdate
以及componentWillUnMount
三个钩子函数的组合。- 当返回了一个函数的时候,这个函数就在
compnentWillUnMount
生命周期调用 - 默认地,传给useEffect的第一个参数会在每次(包含第一次)数据更新时重新调用
- 当给
useEffect()
传入了第二个参数(数组类型)的时候,effect函数会在第一次渲染时调用,其余仅当数组中的任一元素发生改变时才会调用。这相当于我们控制了组件的update生命周期 useEffect()
第二个数组为空则意味着仅在componentDidMount
周期执行一次
- 当返回了一个函数的时候,这个函数就在
- 代码仓库里使用了
Mock.js
拦截api请求以及ant-design
第三UI方库。目前代码比较简陋。