【總結(jié)】1226- 寫好 JSX 條件語句的幾個(gè)建議
大家好,我是 ConardLi。
很多模版語言的框架(比如Vue、Angular)都會內(nèi)置一些條件語法,比如 ng-if、v-if 等,但是在 React 的 JSX 里面,沒有這樣的指令,它提供給我們更靈活的選擇,但是這種靈活也會帶來很多問題,我們今天一起來看幾個(gè)避免這些問題的建議。
小心 0
如果我們渲染的是一個(gè)列表,可能列表里的數(shù)據(jù)不為空的時(shí)候我們才會進(jìn)行渲染,我們可能會寫出下面的判斷代碼:
{data.length?&&?<div>{data.map((d)?=>?d)}div>}
但是,如果 data 數(shù)組是空的,我們會在界面上看到一個(gè) 0。

在 JavaScript 中,布爾運(yùn)算符不會把它們的運(yùn)算結(jié)果轉(zhuǎn)換為布爾值,另外這和 && 的工作方式有關(guān)系,如果左邊是個(gè)假值(比如 0 就是個(gè)假值),會立刻被返回,然后 React 會將這個(gè) 0 放入 DOM 中,如果是布爾值(比如false)就不會。
如果你要使用 && ,永遠(yuǎn)讓左側(cè)的值是個(gè) Boolean 值:
data.length?>?0?&&?jsx
!!data.length?&&?jsx
Boolean(data.length)?&&?jsx
你也可以用三元運(yùn)算符:
{data.length???<div>{data.map((d)?=>?d)}div>?:?null}
注意優(yōu)先級
&& 運(yùn)算符比 || 具有更高的優(yōu)先級,這就意味著你得小心處理同時(shí)包含這兩種運(yùn)算符的 jsx 語句:
你可能會寫出下面的代碼:
data.a?||?data.b?&&?<div?className="error"?/>
這樣寫就錯(cuò)了,上面的代碼實(shí)際上等價(jià)于:
data.a?||?(data.b?&&?<div?className="error"?/>)
根據(jù)以前的經(jīng)驗(yàn),如果你的代碼里有用到 || ,就建議將條件用括號括起來:
(data.a?||?data.b)?&&?<div?className="error"?/>
三運(yùn)算符嵌套地獄
三元運(yùn)算符可以幫助我們很好的切換兩個(gè) JSX,但是一旦超過兩個(gè),你的邏輯很快就會進(jìn)入嵌套地獄:
{isEmoji
??????<EmojiButton?/>
????:?isCoupon
??????????<CouponButton?/>
????????:?isLoaded?&&?<ShareButton?/>}
你可能想把它用 && 重構(gòu)一下,但是也會有一些重復(fù)的判斷條件:
{isEmoji?&&??}?
{isCoupon?&&??}?
{!isEmoji?&&?!isCoupon?&&?isLoaded?&&??}
這時(shí)候,回到原始的 if / else 是個(gè)不錯(cuò)的選擇,建議封裝個(gè)函數(shù):
const?getButton?=?()?=>?{
????if?(isEmoji)?return?<EmojiButton?/>;
????if?(isCoupon)?return?<CouponButton?/>;
????return?isLoaded???<ShareButton?/>?:?null;
};
不要用 JSX 用作判斷條件
通過 props 傳遞的 React 元素能不能用作條件判斷呢,看看下面這個(gè)例子:
const?Wrap?=?(props)?=>?{
????if?(!props.children)?return?null;
????return?<div>{props.children}div>
};
最好不要這樣做,因?yàn)?props.children 可能有幾種不同的情況。
props.children 可能是一個(gè)空數(shù)組,例如 。
那用 children.length 來判斷?也不嚴(yán)謹(jǐn),因?yàn)?children 也可能是單個(gè)元素:。
React.Children.count(props.children) 支持單個(gè)和多個(gè) children,但是,你可能會認(rèn)為 包含兩個(gè)個(gè)元素,而實(shí)際上不是。
我們再來試試通過 React.Children.toArray(props.children) 刪除無效的節(jié)點(diǎn),例如 false。但對于一個(gè)空片段,仍然是正確的:。
所以,為了避免出錯(cuò),最好還是不要用了 ...
重新掛載還是更新?
使用用單獨(dú)的三元運(yùn)算符分支編寫的 JSX 感覺就像是完全獨(dú)立的代碼:
{hasItem???<Item?id={1}?/>?:?<Item?id={2}?/>}
你覺得 hasItem 變化時(shí)會發(fā)生啥?我的猜測是首先 卸載,然后 裝載,因?yàn)槲覍懥藘蓚€(gè)個(gè)單獨(dú)的 JSX 標(biāo)簽。
然而,React 并不知道也不關(guān)心我寫了啥,它所看到的只是 Item 相同位置的元素,所以它依然會保留掛載的實(shí)例,然后更新 props。上面的代碼實(shí)際上等價(jià)于 。
當(dāng)分支包含不同的組件時(shí),比如
{hasItem ?,: } React會重新掛載,因?yàn)?Item1無法更新為Item2。
上面的情況可能問題不大,管理好狀態(tài)就好,可能比重新裝載性能還好。
但是,如果是非受控組件,可能問題就大了:
{mode?===?'name'
??????<input?placeholder="name"?/>
????:?<input?placeholder="phone"?/>}
如果 mode 屬性變化了,你會發(fā)現(xiàn)之前在 name 輸入框輸入的信息還在 ...
通常的解決方案是使用 key,它會告訴 React 這是兩個(gè)完全不一樣的元素:
//?remounts?on?change
{mode?===?'name'
??????<input?placeholder="name"?key="name"?/>
????:?<input?placeholder="phone"?key="phone"?/>}
或者,使用 && 替代三元運(yùn)算符可能會更清晰一點(diǎn):
{mode?===?'name'?&&?"name"?/>?}?
{mode?!==?'name'?&&?"phone"?/>?}
相反,如果你在同一個(gè)邏輯元素上的條件 props 不太一樣,你可以將條件分支拆分為兩個(gè)單獨(dú)的 JSX 標(biāo)簽來提高可讀性:
//?messy
總結(jié)
{number &&會把0渲染出來,可以改為} {number > 0 &&。} 時(shí)刻記得 ||條件周圍的括號:{(cond1 || cond2) &&} 三元運(yùn)算符不要擴(kuò)展到超過 2 個(gè)分支,建議使用 if / else,重構(gòu)不要使用 props.children進(jìn)行條件判斷{condition ?不會重新掛載: } Tag組件,如果你想重新掛載,請使用唯一key或單獨(dú)的&&分支。參考:https://thoughtspile.github.io/2022/01/17/jsx-conditionals/

回復(fù)“加群”與大佬們一起交流學(xué)習(xí)~
