從 loading 的 9 種寫法談 React 業(yè)務開發(fā)

前言
這是一篇比較全面講解 React 的文章,里面很多基礎知識希望你自己一邊查閱資料一邊學習。全文從業(yè)務開發(fā)中最常用見 loading 效果不同是實現(xiàn)講起,說下現(xiàn)在前端開發(fā)在業(yè)務上應該有的思考。
入門級操作
State
最簡單的實現(xiàn),我們在 Loading 組件內部聲明一個狀態(tài),通過代碼邏輯判斷 loading 效果的展示。
export?default?class?extends?Component?{
??...
??render()?{
????return?this.state.loading???<div?className="loader"?/>?:?<div>finishdiv>;
??}
}
完整演示
https://codesandbox.io/s/j22nqo2k4y
Props
隨著業(yè)務的發(fā)展,這個 Loading 組件用到的地方會非常多,上面這個代碼耦合了很多邏輯,為了讓這個組件能夠很好的復用,那我們抽離出組件的業(yè)務邏輯,將內部狀態(tài)進行提升,那這個組件就是一個能被復用的 UI 組件。
export?default?function(props)?{
??return?props.loading???<div?className="loader"?/>?:?<div>finishdiv>;
}
完整演示
https://codesandbox.io/s/k39472w027
注:上面兩段代碼你可能會想,為什么 Func 和 Class 都能實現(xiàn)一個組件,他們有什么差別嗎?
其實你在開發(fā)時不容易感覺到差別,但 React 本身是進行了很多差別處理,如果是 Class 類,React 會用 new 關鍵字實例化,然后調用該實例的 render 方法,如果是 Func 函數(shù),React 會直接調用它。
Refs
如果你是一個 jQuery 轉型 React 的開發(fā),會很自然的想到,我找到 Loading 組件的節(jié)點,控制他的顯示與隱藏,當然這也是可以的,React 提供 Refs 方便你訪問 DOM 節(jié)點 或 React 元素。
export?default?class?extends?Component?{
??componentDidMount()?{
????fetch().then(()?=>?{
??????this.el.changeLoading(false);
????});
??}
??render()?{
????return?(
??????<Loading?ref={el?=>?{?this.el?=?el;?}}?/>
????);
??}
}
完整演示
https://codesandbox.io/s/ywwmm3j46z
通用邏輯抽離
當你的應用做到一定的復雜度,不同的頁面都會有 loading 效果,你肯定不希望每個頁面都重復的書寫一樣的邏輯,這樣會導致你的代碼重復且混亂。
React 中有兩個比較常見的解決方案 HOC 和 Render Props,其實這兩個這兩個概念都是不依賴 React 的。
讓我們暫時忘掉 React,下面我對 HOC 和 Render Props 寫兩個例子,你會發(fā)現(xiàn)組件復用是如此簡單。
HOC
HOC 其實就是一種裝飾器模式,它接受一個組件作為參數(shù),然后返回相同的組件,這樣就可以額外增加一些功能。
const?func?=?()?=>?{
??console.log("func");
};
const?wrap?=?func?=>?{
??console.log("wrap");
??return?func;
};
//?wrap?邏輯已被復用
wrap(func)();
完整演示
https://codesandbox.io/s/8zx85nrzk2
Render Props
Render Props 就是我們給一個函數(shù)傳遞一個回調函數(shù)做為參數(shù),該回調函數(shù)就能利用外面函數(shù)的執(zhí)行結果做為參數(shù),執(zhí)行任何操作。
const?func?=?param?=>?{
??console.log("func");
};
const?wrap?=?(param,?func)?=>?{
??console.log("wrap");
??func(param);
};
//?wrap?邏輯已被復用
wrap("",?func);
完整演示
https://codesandbox.io/s/0v1p4rp7xv
相同點:
兩者都能很好的幫助我們 重用組件邏輯;和回調函數(shù)類似,當嵌套層數(shù)很多時,會造成 回調地獄。
不同點:
HOC 和 父組件有相同屬性名屬性傳遞過來,會造成屬性丟失; Render Props 你只需要實例化一個中間類,而 HOC 你每次調用的地方都需要額外實例化一個中間類。
總的來說,在需要復用組件邏輯的時候,我個人更傾向于 Render Props 的方式。
復雜狀態(tài)管理
當你的應用越來越大,組件之間交互越來越復雜,那整個頁面的數(shù)據(jù)邏輯將變得難以管理,這時候為了方便管理應用的狀態(tài),你可以選擇一些狀態(tài)管理工具,例如 Redux、Flux、dva 等。
Redux

我不太想談這些數(shù)據(jù)流框架,因為他們的概念 action、store、dispatch 太過于生澀難懂。
現(xiàn)代前端框架 React 和 Vue 其實都是一個套路,通過數(shù)據(jù)渲染試圖,然后視圖上操作反過來更新數(shù)據(jù),重新渲染視圖,刷新頁面。數(shù)據(jù)叫做 store,動作叫做 ation,觸發(fā)行為叫 dispatch,然后數(shù)據(jù)到視圖的渲染由 React/Vue 處理的。
(圖片來自 這里)
//?reducers.js
const?initialState?=?{
??loading:?false
};
export?default?function?reducer(state?=?initialState,?action)?{
??switch?(action.type)?{
????case?"CHANGE_LOADING":
??????return?{
????????loading:?action.payload
??????};
????default:
??????return?state;
??}
}
完整演示
https://codesandbox.io/s/94zoy50q6w
Saga
當你代碼中有大量的異步操作時,例如 fetch 請求,你肯定會想到事件監(jiān)聽、回調函數(shù)、發(fā)布/訂閱。很好,上一個例子其實就是事件監(jiān)聽的處理方式,然后回調函數(shù)的主流的解決方案是 redux-thunk,而發(fā)布/訂閱的主流解決方案是 saga。
import?{?takeLatest,?put?}?from?"redux-saga/effects";
import?fetch?from?"./fetch";
function*?fetchInfo(action)?{
??yield?put({
????type:?"CHANGE_LOADING",
????payload:?true
??});
??yield?fetch();
??yield?put({
????type:?"CHANGE_LOADING",
????payload:?false
??});
}
export?default?function*?fetchSaga()?{
??yield?takeLatest("FETCH_REQUEST",?fetchInfo);
}
完整演示
https://codesandbox.io/s/rrnp9vk3wp
當你耐心看到這里,我知道你對 React 肯定有一定的經驗,現(xiàn)在還可以做很多,例如把 loading 狀態(tài)提升到 Store 的頂部,那整個站點就只有一個 loading 了,然后你還可以將 fetch 再封裝一個 HOC 修改 loading 狀態(tài),這就是一個相對完美的 loading,其實 React 業(yè)務開發(fā)都可以用這個套路。
新的 API
Context

上面 redux 的例子是不是過于復雜 對于簡單的業(yè)務,雖然有很多頁面,嵌套層次也很復雜,你當然可以不用狀態(tài)管理工具,你可以試著使用 Context,它可以方便你傳遞數(shù)據(jù),它其實就是 Render Props 的一種實現(xiàn)。
export?default?React.createContext({
??loading:?false,
??changeLoading:?()?=>?{}
});
完整演示
https://codesandbox.io/s/6lp0p7z4jz
Hooks
寫到這,靜一下,是不是哪里做錯了什么?
我的業(yè)務只是想寫個簡單的 loading 效果,卻了解了一堆組件生命周期的概念。
Hooks 剛好幫你解決了這樣的問題,Hooks 能允許你通過執(zhí)行單個函數(shù)調用來使用函數(shù)中的 React 功能,讓你把面向生命周期編程變成面向業(yè)務邏輯編程。
import?React,?{?useState,?useEffect?}?from?"react";
import?Loading?from?"./Loading/index";
import?fetch?from?"./fetch";
function?App()?{
??const?[loading,?setLoading]?=?useState(true);
??useEffect(()?=>?{
????fetch().then(()?=>?{
??????setLoading(false);
????});
??},?[]);
??return?<Loading?loading={loading}?/>;
}
export?default?App;
完整演示
https://codesandbox.io/s/98m4j00vwo
好好總結
上面對每個點都做了具體實現(xiàn),但他們都不是隔離的,你可以根據(jù)你的認知和業(yè)務特點總結抽象一套自己的方法論;
多了解、多抽象、多思考,練就十八般武藝,遇到問題的時候才能游刃有余;
React 現(xiàn)在宣傳的東西越來越多,你最好先深入了解他們,然后用批判的眼光,保持理智,防止自己每天用很新的特性重構你自己的代碼。
參考文章
React 官網(wǎng):https://reactjs.org/
When do I know I’m ready for Redux?:https://medium.com/dailyjs/when-do-i-know-im-ready-for-redux-f34da253c85f
文章可隨意轉載,但請保留此 原文鏈接。非常歡迎有激情的你加入 ES2049 Studio,簡歷請發(fā)送至 caijun.hcj(at)alibaba-inc.com 。

我是依揚,螞蟻集團-保險團隊正在大量招聘中,詳情見:我們是螞蟻保險前端團隊,我們今年在做什么,有興趣快來聯(lián)系我吧[email protected]
