javascript
之前写的 JSX 的条件语句竟然存在那么多 Bug?
作者 | 零一
來源 |?前端印象
今天的主題是:關于 JSX 的條件語句,你不知道3件事
一、&&隱藏大坑
在 JSX 里寫條件語句,&& 應該是用的最多的了,例如:
function?Demo?()?{//?...省略一些代碼return?(<div>{isShow?&&?<Child/>}</div>) }這樣寫確實非常簡單易懂,但也存在隱藏的踩坑點,那就是 &&邏輯運算符的工作原理
&& 邏輯運算符工作原理: 例如 A && B ,當 A 隱式轉換后為 true 時,則返回 B;當 A 隱式轉換后為 false 時,則返回 A
舉個例子🌰:
const?A?=?0 const?B?=?1const?C?=?A?&&?B???//?0 const?D?=?B?&&?A???//?0所以有一種場景下,我們用 && 符號做條件判斷渲染會有問題:有一個列表,當有列表數據時,展示列表里的內容;當沒有列表數據時,則什么都不展示
function?List?()?{//... }function?App?()?{const?[list,?setList]?=?useState([])useEffect(()?=>?{//?請求列表數據//?...},?[])return?(<div>{list.length?&&?<List?data={list}?/>}</div>) }代碼看起來沒什么問題,邏輯也說得通(當 list 有具體數據時,展示 <List/> 組件),但其實是有問題的,此時頁面長這個樣:
為什么? 這就是剛才提到的 && 的工作原理了,當咱們未請求數據前,list = [] ,即 list.length = 0,那么 list.length && <List data={list} /> ?最終返回的就是 0 了,所以自然而然的 0 就出現在了頁面中
這一定不是你想要的,下面提出一些解決方案和建議吧:
用三元運算符,即 list.length ? <List data={list} /> : null
兩次取反,即 !!list.length && <List data={list} />
直接給出具體的判斷邏輯,即 list.length > 0 && <List data={list} />
當然了,如果判斷條件本來就是布爾值的話,那就可以忽略這一條了
二、Children作判斷條件
在某些場景下我們可能會寫一個組件來處理邏輯,例如:
function?Wrap?(props)?{if?(props.children)?{return?(<div><p>當前內容為:</p><div>{props.children}</div></div>)}?else?{return?(<div>nothing</div>)} }function?App?()?{return?(<Wrap><div>零一</div></Wrap>) }這段代碼看起來也是毫無問題(當有傳遞給 <Wrap/> 組件 children 屬性時,直接展示內容;否則展示 nothing ,表示當前為空),但其實存在很多漏洞情況,例如:
function?App?()?{return?(<Wrap>{list.map(item?=>?<span>{item}</span>)}</Wrap>) }假設此時變量list 為 [] ,那么 Wrap 組件中接收到的 children 則也為 [],那么 if (props.children) 的判斷結果也為 true,則頁面會這樣展示:
這顯然不是我們想要的結果。我們想要的效果是:當接收到空數組時,也展示 nothing ,即為空
有什么解決方案呢?
React 提供了現成的用于處理 children的 API:
React.Children.map
React.Children.forEach
React.Children.count
React.Children.only
React.Children.toArray
這里就不一一介紹每個的作用了,想要了解的可以直接去官網看:https://zh-hans.reactjs.org/docs/react-api.html#reactchildren
我們直接挑重點說,可以直接用 React.Children.toArray 來做處理,該方法可以把 children 統一變成數組的形式
還是用剛才的那個例子,我們改造一下看看返回了什么:
import?{?Children?}?from?'react'function?Wrap?(props)?{//?用?Children.toArray?來處理?props.childrenif?(Children.toArray(props.children).length)?{return?(<div><p>當前內容為:</p><div>{props.children}</div></div>)}?else?{return?(<div>nothing</div>)} }function?App?()?{return?(<Wrap>{?//?返回空數組[].map(item?=>?<span>{item}</span>)}</Wrap>) }此時頁面展示的是:
為什么會這樣呢?打個斷點進去看了一下 React.Children.toArray 大致都做了什么處理,這里簡單總結一下:將 children 傳過來的每個元素都放到一個數組中再返回,并會過濾掉空數組、Boolean、undefined
所以我們剛才的例子中,空數組直接被過濾掉了。我們再來驗證一下 React.Children.toArray 的強大,舉個例子🌰
function?App?()?{return?(<Wrap>{false?&&?<span>作者:零一</span>}{true}{?//?返回空數組[].map(item?=>?<span>{item}</span>)}{{}?.name}</Wrap>) }這種情況,<Wrap/> 組件接收到的 children 值應為:
[false,true,[],undefined, ]那么頁面展示的是什么呢?
是的,還是nothing,因為這四種情況的值全都被 React.Children.toArray 給過濾掉了,最終返回的值為 [] ,這也十分符合我們開發時的預期
所以如果你真的需要把 children 作為條件判斷的依據的話,我建議是用這個方法!
三、掛載與更新
三元運算符在 JSX 中經常被我們拿來用于兩種不同狀態的組件切換,例如:
import?{?Component,?useState?}?from?'react'class?Child?extends?Component?{componentDidMount()?{console.log('掛載',?this.props.name,?this.props.age);}componentDidUpdate()?{console.log('更新',?this.props.name,?this.props.age);}render?()?{const?{?name,?age?}?=?this.propsreturn?(<div><p>{name}</p><p>{age}</p></div>)} }function?App?()?{const?[year,?setYear]?=?useState('1999')return?(<div>{?year?===?'1999'???<Child?name="零一"?age={1}?/>:?<Child?name="01"?age={23}?/>}<button?onClick={()?=>?{setYear(year?===?'1999'???'2022'?:?'1999')}}>切換</button></div>) }看到這個代碼,你是不是覺得當變量 year 切換時,一個組件會卸載,另一個組件會掛載?但其實不是,我們來驗證一下:
可以看到,我們在切換了變量 year 時,<Child/> 組件只掛載了一次,而不是不停地掛載、卸載。其實這是React做的處理,雖然寫了兩個 <Child/> 組件,但React只認為是一個,并直接進行更新,即上述代碼等價于:
//?...?省略大部分代碼 function?App?()?{//?...return?(<div><Child?name={year?===?'1999'???"零一"?:?"01"}?age={year?===?'1999'???1?:?23}?/>//?...</div>) }這種情況需要特別注意,當你真的想寫兩次同一個組件并傳遞不同的參數時,你可以給這兩個組件賦予不同的 key ,那么React就不會認為它倆是同一個組件實例了,例如:
function?App?()?{//?...return?(<div>{?year?===?'1999'???<Child?name="零一"?age={1}?key="0"/>:?<Child?name="01"?age={23}?key="1"/>}</div>) }如果本意就是不想讓兩個組件實例不停卸載和掛載,那么就不需要做額外操作了~
往期推薦
好難啊……一個 try-catch 問出這么多花樣
k8s集群居然可以圖形化安裝了?
用了HTTPS,沒想到還是被監控了
將 k8s 制作成 3D 射擊游戲,好玩到停不下來
點分享
點收藏
點點贊
點在看
總結
以上是生活随笔為你收集整理的之前写的 JSX 的条件语句竟然存在那么多 Bug?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 完全无人驾驶量产车Apollo Moon
- 下一篇: 【SpringCloud-Alibaba