原文:React Hooks使用實例(二)| AlloyTeam
作者:TAT.zhongzhong
在上篇文章我們講了如何使用React的Suspense組件和lazy方法來實現模塊的懶加載,後面還講了如何使用
React的useState方法來實現自定義的Hooks,從而達到複用的目的。
我們知道,不管在做什麼樣的前端項目,列表頁肯定是存在的,那如何獲取列表的數據呢?大部分情況下我們都是在每個模塊內部自己實現一個獲取數據的方法,然後調用setState來更新數據。那有沒有更好的方式可以做到這些,並且能夠在一個項目中處處複用這個功能呢?答案就是使用React Hooks。
useEffect介紹
簡單的說,useEffect就是在組件掛載完成或者更新完成的時候,需要執行的一系列操作,這些操作可能是ajax請求,dom操作,事件處理等等。
官方文檔裏面有句話說的是,useEffect是 componentDidMount, componentDidUpdate, 和 componentWillUnmount三個生命週期鉤子的組合,那就是之前分別在這三個地方乾的事情,現在可以統一在一個地方幹了,是不是很方便?。爲了保證文章簡潔,這裏不過多介紹,有需要可以參考官方文檔
useReducer介紹
如果你用過redux,那你應該知道redux就是通過reducer來處理dispatch出來的各種action的。每個reducer都是一個純函數,處理完成之後,返回新的state,然後觸發React的更新。官方文檔
自定義一個獲取數據的React Hooks
先來分析下,我們要從服務器獲取數據,需要做哪些事情。
- 構造請求參數
- 發送請求
- 解析返回結果或異常處理
- 展示結果或異常錯誤提示
從上面的幾個點我們可以分析出,我們的自定義Hook要能夠傳入請求人url以及請求的參數,在請求失敗的時候能夠有後臺返回的提示信息,在請求成功的時候能夠返回後臺返回的數據,我們還需要知道請求是否失敗。
這裏我們將action拆分爲3個:
- FETCH_INIT // 開始加載數據,用來展示Loading狀態
- FETCH_SUCCESS // 加載數據成功,用來展示數據
- FETCH_ERROR // 加載數據失敗,用來展示錯誤信息
基於上面的分析,我們先定義一個reducer,用來處理每個action。
reducer.ts
export const dataFetchReducer = (state: any, action: {[type: string]: any}) => {
switch(action.type) {
case 'FETCH_INIT':
return {
...state,
isLoading: true,
isError: false
}
case 'FETCH_SUCCESS':
return {
...state,
isLoading: false,
isError: false,
data: action.payload
}
case 'FETCH_ERROR':
return {
...state,
isLoading: false,
isError: true,
msg: action.payload
}
default:
throw new Error(`Unsupport action type:${action.type}`);
}
}
上面的reducer非常簡單,就是處理上面我們定義的三個action,然後每次都返回一個新的state,解釋下上面返回的state的各個字段的用意:
- isLoading: 是否展示加載中的提示,比如我們請求正在處理,那需要有一個提示信息給到用戶
- isError: 請求是否失敗,如果這個值爲true的話,那頁面就要展示msg中的錯誤提示信息
- msg: 錯誤提示信息,當isError爲true的時候不爲空
介紹了reducer之後,我們來看下Hook是什麼實現的:
代碼如下:
interface RequestConfig extends AxiosRequestConfig {
url: string
}
export const useDataApi = (initData: Array<any> | any, initRequestConfig: RequestConfig) => {
if (!initRequestConfig.method) {
initRequestConfig.method = 'get';
}
const [requestConfig, setRequestConfig] = useState(initRequestConfig);
const [state, dispatch] = useReducer(dataFetchReducer, {
data: initData,
isLoading: false,
isError: false
});
useEffect(() => {
const fetchData = async () => {
try{
dispatch({
type: 'FETCH_INIT'
});
if (!requestConfig.url) {
dispatch({
type: 'FETCH_SUCCESS',
payload: []
});
}else {
const response = await axios(requestConfig).catch(e => {
return e.response;
});
if (response.data){
const data = response.data;
const { success, result, message } = data;
if (!success) {
dispatch({
type: 'FETCH_ERROR',
payload: message
});
} else {
dispatch({
type: 'FETCH_SUCCESS',
payload: result
});
}
}else {
dispatch({
type: 'FETCH_ERROR',
msg: '加載數據失敗'
});
}
}
}catch(e) {
dispatch({
type: 'FETCH_ERROR',
msg: '加載失敗'
});
}
};
fetchData();
}, [requestConfig]);
return [state, setRequestConfig];
}
在上面的代碼中,我們定義了一個自定義的Hooks,名稱爲useDataApi,這個Hooks有2個參數,第一個參數是表示初始化時候的數據,第二個參數就是我們請求需要用到的各種參數了。
上面的useState我們就不多介紹了,主要來介紹下,下面代碼中的useReducer。
const [state, dispatch] = useReducer(dataFetchReducer, {
data: initData,
isLoading: false,
isError: false
});
這裏useReducer的第一個參數,就是傳入我們剛剛定義好的reducer,那個reducer裏面我們處理了3中類型的action,對不對?
第二個參數就是傳入一個初始化的satte對象了。
然後看下這裏的返回值,他是一個數組,爲什麼要返回一個數組呢?
因爲這樣通過解構之後,你可以隨意命名他們的兩個返回值(一本正經)。
在上面的兩個返回值中,第一個state是我們渲染的時候需要用到的,第二個dispatch用來分發action的,這個dispatch分發的actio你,就會被我們自定義的reducer去處理。
然後再來看下下面的useEffect的代碼,這裏代碼比較多,我們一點點看:
dispatch({
type: 'FETCH_INIT'
});
上面的代碼主要用來分發一個FETCH_INIT
的action,這個時候就回返回isLoading爲true的state,組件根據這個來展示loading或者加載中...等提示。
const response = await axios(requestConfig).catch(e => {
return e.response;
});
這段代碼就是具體的發送請求的代碼,這裏我使用了axios這個庫,當然你也可以替換成別的庫。
在請求完成之後,需要解析返回的結果,然後來決定是觸發FETCH_SUCCESS
還是觸發FETCH_ERROR
action.
const data = response.data;
const { success, result, message } = data;
if (!success) {
dispatch({
type: 'FETCH_ERROR',
payload: message
});
} else {
dispatch({
type: 'FETCH_SUCCESS',
payload: result
});
}
這段代碼,我們獲取response的data,然後解析data的數據,這裏data就是後臺返回的數據結構了,我這裏後臺返回的格式就是會包括這三個字段,success標識請求是否成功,result標識請求的結果,message表示提示信息,一般用來返回錯誤的提示信息。
到這裏我們自定義的Hooks就算完成了,然後我們來看下怎麼使用這個Hooks來加載數據。
使用自定義的Hooks來加載數據
const [{ isLoading, isError, msg, data }, setRequestConfig] = useDataApi([], {
url: apiPath
});
if (isError) {
message.error(msg);
}
return (
<div>
<div className="toolbar">
<Link to={window.location.pathname + "/detail"}>
<Button type="primary" icon='plus'>
添加
</Button>
</Link>
</div>
<CustomModal />
<Table columns={columns}
dataSource={data}
loading={isLoading}
rowKey={'id'}
/>
</div>
);
看上面的代碼,是不是足夠簡單了?而且你可以再任何地方使用這個自定義的Hooks。
總結
本篇文章主要講了以下幾個點:
- useEffect和useReducer簡單介紹
- 實現自定義的獲取數據的Hooks