前言
我已經(jīng)使用 React 多年,我確信我非常了解它,但最近我的老板對我說,“你根本不知道 React,你對它一無所知?!?/p>
我很生他的氣,但他指出了我程序中的三個漏洞。我現(xiàn)在把它記錄下來,也分享給還不知道的小伙伴。
1、你知道“&&”的用法嗎?
在React程序中,我經(jīng)常使用“&&”運算符來決定是否顯示內(nèi)容,具體方式如下:
const App = () => {
const [ list, setList ] = useState([])
// Simulation request data
setTimeout(() => {
setList([ 'fatfish', 'medium' ])
}, 2000)
return (
<div className="app">{ list.length && <List /> }</div>
)
}
我老板:“你不知道&&”運算符的特點嗎?當請求還沒有成功返回時,會直接渲染“0”。
我不服氣,因為我一直都是這樣寫代碼,從來沒有犯過錯誤。為了證明老大錯了,我寫了下面的例子。
const List = ({ list = [] }) => {
return (
<div className="name-list-container">
{
list.map((name) => {
return <div className="name-list-item">{ name }</div>
})
}
</div>
)
}
const App = () => {
const [ list, setList ] = React.useState([ ])
// Simulation request data
setTimeout(() => {
setList([ 'fatfish', 'medium' ])
}, 3000)
return (
list.length && <List list={ list }/>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
我的天??!老大說的對,一開始頁面顯示0,3秒后顯示列表。
為什么?
來自 MDN的提示:“當且僅當所有操作數(shù)都為真時,一組布爾操作數(shù)的邏輯與 (&&) 運算符(邏輯合取)才為真。否則就是假的?!?/p>
更一般地,運算符返回從左到右計算時遇到的第一個假操作數(shù)的值,或者如果它們都是真值,則返回最后一個操作數(shù)的值。
例子如下:
const x1 = 0
const x2 = 'fatfish'
const x3 = 1
const x4 = 'medium'
console.log(x1 && x2) // 0
console.log(x3 && x4) // medium
現(xiàn)在我終于明白為什么寫這樣的代碼會導致錯誤。原因如下:
list.length && <List list={ list } />
0 && <List list={ list } /> // 0
如何解決?
我找到了三種方法來解決這個問題。我希望你不要犯和我一樣的錯誤,祝福你。
// 1. Convert list.length to boolean
!!list.length && <List list={ list }/>
// 2. Use ternary expressions and null
list.length ? <List list={ list }/> : null
// 3. Controlled by specific logic
list.length >= 1 && <List list={ list }/>
2.“props.children”的奇怪行為
我猜你寫過類似的代碼。當向 <Container /> 組件傳遞內(nèi)容時,會顯示“children”。如果沒有,將顯示一個空的工具提示。像下面這樣:
const Container = ({ children }) => {
if (children) {
return (
<div className="children-container">
<p>The content of children is:</p>
{ children }
</div>
)
} else {
return (
<div className="empty">empty</div>
)
}
}
我的老板:“你要小心使用‘children’屬性,它會導致邏輯異常!就像在以下情況中一樣?!?/p>
1).清空列表數(shù)據(jù)
你認為這個例子會顯示什么——“空”?
不幸的是,答案是另一個。你是不是也覺得不可思議?朋友們,我們一定要非常小心地使用 props.children。否則,老板可能會扣你的工資。

const Container = ({ children }) => {
if (children) {
return (
<div className="children-container">
<p>The content of children is:</p>
{ children }
</div>
)
} else {
return (
<div className="empty">empty</div>
)
}
}
const App = () => {
const [ list, setList ] = React.useState([])
return (
<Container>
{
list.map((name) => {
return <div className="name-item">{ name }</div>
})
}
</Container>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
為什么?
讓我們向“Container”組件添加一行代碼,并嘗試打印children是什么!
const Container = ({ children }) => {
console.log(children, 'children')
// ...
}
是的,你是對的。此時“children”為空數(shù)組,所以顯示“children的內(nèi)容為:”而不是“empty”。

如何解決?
使用 React.Children.toArray 解決這個問題會很容易,然后你會看到顯示“empty”。所以如果你真的需要用children作為條件判斷,我建議你使用這個方法!
const Container = ({ children }) => {
// if (children) {
// Pay attention here
if (React.Children.toArray(children).length) {
return (
<div className="children-container">
<p>The content of children is:</p>
{ children }
</div>
)
} else {
return (
<div className="empty">empty</div>
)
}
}
3.關于掛載和更新的問題
在 React 中通過狀態(tài)來切換組件是很常見的,但是,這個小東西也會讓你感到困惑。
在下面的代碼中,你認為當你切換name的值時,一個Demo組件會被卸載,另一個會被掛載嗎?
class Demo extends React.Component {
componentDidMount() {
console.log('componentDidMount', this.props.name);
}
componentDidUpdate() {
console.log('componentDidUpdate', this.props.name);
}
render () {
return (
<div>
{ this.props.name }
</div>
)
}
}
const App = () => {
const [ name, setName ] = React.useState('fatfish')
const onClick = () => {
setName(name === 'fatfish' ? 'medium' : 'fatfish')
}
return (
<div className="app">
{
name === 'fatfish' ?
<Demo name={ name } /> :
<Demo name={ name } />
}
<button onClick={ onClick }>click</button>
</div>
)
}
ReactDOM.render(<App />, document.getElementById('app'))
我錄制了一個簡短的 gif 給你真相。

你也可以通過 CodePen 試試,https://codepen.io/qianlong/pen/NWywodV
為什么?
雖然,我們寫了兩個 Demo 組件,假設它們會分別掛載和更新,但 React 認為它們是同一個組件,所以 componentDidMount 只會執(zhí)行一次。
如何解決?
但是當我們要寫兩個相同的組件但是傳遞不同的參數(shù)時,我們應該怎么辦呢?
是的,你應該為這兩個組件添加不同的鍵,這樣 React 就會認為它們是不同的組件。componentDidMount 也會單獨執(zhí)行。
我們試試看:
//...
// Pay attention here
name === 'fatfish' ? <Demo key="1" name={ name } /> : <Demo key="2" name={ name } />
//...

你也可以通過 CodePen 試試,https://codepen.io/qianlong/pen/NWywodV。