1 引言
本期精讀的文章是:8 React conditional rendering methods
介紹了八種 React 條件渲染方式。
模版條件渲染非常常見,遇到的時候往往會隨機選擇一種方式使用,那麼怎麼寫會有較好的維護性呢?先一起了解下有哪八種條件渲染方式吧!
2 概述
IF/ELSE
既然 JSX 支援 js 與 html 混寫,那麼交替使用就能解決條件渲染的問題:
function render() {
if (renderComponent1) {
return <Component1 />;
} else {
return <div />;
}
}
複製程式碼
return null
如果不想渲染空元素,最好使用 null
代替空的 div
:
function render() {
if (renderComponent1) {
return <Component1 />;
} else {
return null;
}
}
複製程式碼
這樣對 React 渲染效率有提升。
元件變數
將元件賦值到變數,就可以在 return 前任意修改它了。
function render() {
let component = null;
if (renderComponent1) {
component = <Component1 />;
}
return component;
}
複製程式碼
三元運算子
三元運算子的語法如下:
condition ? expr_if_true : expr_if_false
複製程式碼
用在 JSX 上也很方便:
function render() {
return renderComponent1 ? <Component1 /> : null;
}
複製程式碼
但三元運算子產生巢狀時,理解成本會變得很高。
&&
這個是最常用了,因為程式碼量最少。
function render() {
return renderComponent1 && <Component1 />;
}
複製程式碼
IIFE
IIFE 含義是立即執行函式,也就是如下程式碼:
(function myFunction(/* arguments */) {
// ...
})(/* arguments */);
複製程式碼
當深陷 JSX 程式碼中,又想寫一大塊邏輯時,除了回到上方,還可以使用 IIFE:
function render() {
return (
<div>
{(() => {
if (renderComponent1) {
return <Component1 />;
} else {
return <div />;
}
})()}
</div>
);
}
複製程式碼
子元件
這是 IIFE 的變種,也就是把這段立即執行函式替換成一個普通函式:
function render() {
return (
<div>
<SubRender />
</div>
);
}
function SubRender() {
if (renderComponent1) {
return <Component1 />;
} else {
return <div />;
}
}
複製程式碼
IF 元件
做一個條件渲染元件 IF
代替 js 函式的 if
:
<If condition={true}>
<span>Hi!</span>
</If>
複製程式碼
這個元件實現也很簡單
const If = props => {
const condition = props.condition || false;
const positive = props.then || null;
const negative = props.else || null;
return condition ? positive : negative;
};
複製程式碼
高階元件
高階元件,就是返回一個新元件的函式,並且接收一個元件作為引數。
那麼我們就能在高階元件裡寫條件語句,返回不同的元件即可:
function higherOrderComponent(Component) {
return function EnhancedComponent(props) {
if (condition) {
return <AnotherComponent {...props} />;
}
return <Component {...props} />;
};
}
複製程式碼
3 精讀
這麼多方法都能實現條件渲染,那麼重點在於可讀性與可維護性。
比如通過呼叫函式實現元件渲染:
<div>{renderButton()}</div>
複製程式碼
看上去還是比較冗餘,如果使用 renderButton
getter 定義,我們就可以這麼寫它:
<div>{button}</div>
複製程式碼
其實我們想要的就是 button,而不是 renderButton
。那麼還可以進一步,乾脆封裝成 JSX 元件:
<div>
<Button />
</div>
複製程式碼
是否要付出這些努力,取決於應用的複雜度。如果應用複雜度非常高,那你應當儘量使用最後一種封裝,讓每個檔案的邏輯儘量獨立、簡單。
如果應用複雜度比較低,那麼注意不要過度封裝,以免把自己繞進去。
所以看來這又是一個沒有固定答案的問題,選擇何種方式封裝,取決於應用複雜度。
應用複雜度
對任何程式碼封裝,都會增加這段 連線邏輯 的複雜度。
假定無論如何程式碼的複雜度都是恆定不變的,下面這段程式碼,連線複雜度為 0,而對於 render
函式而言,邏輯複雜度是 100:
function render() {
if (renderComponent) {
return isOk ? <Component1 /> : <Component2 />;
} else {
return <div />;
}
}
複製程式碼
下面這段程式碼拆成了兩個函式,邏輯複雜度對 render
SubComponent
來說都是 50,但連線複雜度是 50:
function render() {
if (renderComponent) {
return <SubComponent>;
} else {
return <div />;
}
}
function SubComponent() {
return isOk ? <Component1 /> : <Component2 />
}
複製程式碼
可以看到,我們通過函式拆分,降低了每個函式的邏輯複雜度,但卻提高了連線複雜度。
下面來做一個比較,我們假設一個正常的程式設計師,可以一次性輕鬆記憶 10 個函式。如果再多,函式之間的呼叫關係就會讓人摸不著頭腦。
應用較小時
在應用程式碼量比較小時,假設一共有 10 個函式,如果做了邏輯抽象,拆分出了 10 個子函式,那麼總邏輯複雜度不變,函式變成了 20 個。
此時小王要修改此專案,他需要找到關鍵程式碼的位置。
如果沒有做邏輯抽象,小王一下子就記住了 10 個函式,並且很快完成了需求。
如果應用做了邏輯抽象,他需要理解的邏輯複雜度是不變的,但是要讀的函式變成了 20 個。小王需要像偵探一樣線上索中不斷跳轉,他還是隻找了 10 個關鍵函式,但一共也就 20 個函式,邏輯並不複雜,這值得嗎?
小王心裡可能會嘀咕:簡單的邏輯瞎抽象,害我檔案找了半天!
應用較大時
此時應用程式碼量比較大,假設一共有 500 個函式,我們不考慮抽象後帶來的複用好處,假設都無法複用,那麼做了邏輯抽象後,那麼總邏輯複雜度不變,函式變成了 1000 個。
此時小王接到了需求,終於維護了一個大專案。
小王知道這個專案很複雜,從一開始就沒覺得能理解專案全貌,所以把自己當作一名偵探,準備一步步探索。
現在有兩種選擇,一種是在未做邏輯抽象時探索,一種是在做過邏輯抽象後探索。
如果沒做邏輯抽象,小王需要面對 500
個這種函式:
function render() {
if (renderComponent) {
return isOk ? <Component1 /> : <Component2 />;
} else {
return isReady ? <Component3 /> : <Component4 />;
}
}
複製程式碼
如果做了邏輯抽象,小王需要面對 1000
個這種函式:
function render() {
if (renderComponent) {
return <Component1And2 />;
} else {
return <Component3And4 />;
}
}
複製程式碼
在專案龐大後,總函式數量並不會影響對線索的查詢,而匯流排索深度也幾乎總是固定的,一般在 5 層左右。
小王理解 5 個或 10 個函式成本都差不多,但沒有做邏輯抽象時,這 5 個函式各自參雜了其他邏輯,反而影響對函式的理解。
這時做邏輯抽象是合適的。
4 總結
所以總的來說,筆者更傾向使用子函式、子元件、IF 元件、高階元件做條件渲染,因為這四種方式都能提高程式的抽象能力。
往往抽象後的程式碼會更具有複用性,單個函式邏輯更清晰,在切面程式設計時更利於理解。
當專案很簡單時,整個專案的理解成本都很低,抽象帶來的複雜度反而讓專案變成了需要切面程式設計的時候,就得不償失了。
總結一下:
- 當專案很簡單,或者條件渲染的邏輯確認無法複用時,推薦在程式碼中用
&&
或者三元運算子、IIFE 等直接實現條件渲染。 - 當專案很複雜時,儘量都使用 子函式、子元件、IF 元件、高階元件 等方式做更有抽象度的條件渲染。
- 在做邏輯抽象時,考慮下專案的複雜度,避免因為抽象帶來的成本增加,讓本可以整體理解的專案變得支離破碎。
5 更多討論
如果你想參與討論,請點選這裡,每週都有新的主題,週末或週一釋出。