【React】730- 從 loading 的 9 種寫法談 React 業(yè)務(wù)開發(fā)

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

我不太想談這些數(shù)據(jù)流框架,因?yàn)樗麄兊母拍?action、store、dispatch 太過于生澀難懂。
現(xiàn)代前端框架 React 和 Vue 其實(shí)都是一個(gè)套路,通過數(shù)據(jù)渲染試圖,然后視圖上操作反過來更新數(shù)據(jù),重新渲染視圖,刷新頁面。數(shù)據(jù)叫做 store,動(dòng)作叫做 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
當(dāng)你代碼中有大量的異步操作時(shí),例如 fetch 請求,你肯定會(huì)想到事件監(jiān)聽、回調(diào)函數(shù)、發(fā)布/訂閱。很好,上一個(gè)例子其實(shí)就是事件監(jiān)聽的處理方式,然后回調(diào)函數(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
當(dāng)你耐心看到這里,我知道你對 React 肯定有一定的經(jīng)驗(yàn),現(xiàn)在還可以做很多,例如把 loading 狀態(tài)提升到 Store 的頂部,那整個(gè)站點(diǎn)就只有一個(gè) loading 了,然后你還可以將 fetch 再封裝一個(gè) HOC 修改 loading 狀態(tài),這就是一個(gè)相對完美的 loading,其實(shí) React 業(yè)務(wù)開發(fā)都可以用這個(gè)套路。
新的 API
Context

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

回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
點(diǎn)擊“閱讀原文”查看 80+ 篇原創(chuàng)文章
