原文: React Hooks使用實例(二)| AlloyTeam
做者:TAT.zhongzhong
在上篇文章咱們講了如何使用React的Suspense組件和lazy方法來實現模塊的懶加載,後面還講了如何使用
React的useState方法來實現自定義的Hooks,從而達到複用的目的。html
咱們知道,無論在作什麼樣的前端項目,列表頁確定是存在的,那如何獲取列表的數據呢?大部分狀況下咱們都是在每一個模塊內部本身實現一個獲取數據的方法,而後調用setState來更新數據。那有沒有更好的方式能夠作到這些,而且可以在一個項目中到處複用這個功能呢?答案就是使用React Hooks。前端
簡單的說,useEffect就是在組件掛載完成或者更新完成的時候,須要執行的一系列操做,這些操做多是ajax請求,dom操做,事件處理等等。react
官方文檔裏面有句話說的是,useEffect是 componentDidMount, componentDidUpdate, 和 componentWillUnmount三個生命週期鉤子的組合,那就是以前分別在這三個地方乾的事情,如今能夠統一在一個地方幹了,是否是很方便?。爲了保證文章簡潔,這裏不過多介紹,有須要能夠參考官方文檔ios
若是你用過redux,那你應該知道redux就是經過reducer來處理dispatch出來的各類action的。每一個reducer都是一個純函數,處理完成以後,返回新的state,而後觸發React的更新。官方文檔ajax
先來分析下,咱們要從服務器獲取數據,須要作哪些事情。redux
從上面的幾個點咱們能夠分析出,咱們的自定義Hook要可以傳入請求人url以及請求的參數,在請求失敗的時候可以有後臺返回的提示信息,在請求成功的時候可以返回後臺返回的數據,咱們還須要知道請求是否失敗。axios
這裏咱們將action拆分爲3個:api
基於上面的分析,咱們先定義一個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的各個字段的用意:
介紹了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來加載數據。
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。
本篇文章主要講了如下幾個點:
AlloyTeam 歡迎優秀的小夥伴加入。
簡歷投遞: alloyteam@qq.com
詳情可點擊 騰訊AlloyTeam招募Web前端工程師(社招)