前言 我已经使用 React 多年,我确信我非常了解它,但最近我的老板对我说,“你根本不知道 React,你对它一无所知。”
我很生他的气,但他指出了我程序中的三个漏洞。我现在把它记录下来,也分享给还不知道的小伙伴。
1、你知道“&&”的用法吗? 在React程序中,我经常使用“&&”运算符来决定是否显示内容,具体方式如下:
复制 const App = () => {
const [ list , setList ] = useState ([])
// Simulation request data
setTimeout (() => {
setList ([ 'fatfish' , 'medium' ])
}, 2000 )
return (
< div className = "app" > { list .length && < List / > }< /div>
)
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 我老板:“你不知道&&”运算符的特点吗?当请求还没有成功返回时,会直接渲染“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' ))
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 我的天啊!老大说的对,一开始页面显示0,3秒后显示列表。
为什么? 来自 MDN的提示:“当且仅当所有操作数都为真时,一组布尔操作数的逻辑与 (&&) 运算符(逻辑合取)才为真。否则就是假的。”
更一般地,运算符返回从左到右计算时遇到的第一个假操作数的值,或者如果它们都是真值,则返回最后一个操作数的值。
例子如下:
复制 const x1 = 0
const x2 = 'fatfish'
const x3 = 1
const x4 = 'medium'
console .log (x1 && x2 ) // 0
console .log (x3 && x4 ) // medium
现在我终于明白为什么写这样的代码会导致错误。原因如下:
复制 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 /> 组件传递内容时,会显示“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>
)
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 我的老板:“你要小心使用‘children’属性,它会导致逻辑异常!就像在以下情况中一样。”
1).清空列表数据 你认为这个例子会显示什么——“空”?
不幸的是,答案是另一个。你是不是也觉得不可思议?朋友们,我们一定要非常小心地使用 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' ))
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 为什么? 让我们向“Container”组件添加一行代码,并尝试打印children是什么!
复制
const Container = ({ children }) => {
console .log (children , 'children' )
// ...
}
是的,你是对的。此时“children”为空数组,所以显示“children的内容为:”而不是“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>
)
}
}
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 3.关于挂载和更新的问题 在 React 中通过状态来切换组件是很常见的,但是,这个小东西也会让你感到困惑。
在下面的代码中,你认为当你切换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' ))
1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. 30. 31. 32. 33. 34. 我录制了一个简短的 gif 给你真相。
你也可以通过 CodePen 试试,https://codepen.io/qianlong/pen/NWywodV
为什么? 虽然,我们写了两个 Demo 组件,假设它们会分别挂载和更新,但 React 认为它们是同一个组件,所以 componentDidMount 只会执行一次。
如何解决? 但是当我们要写两个相同的组件但是传递不同的参数时,我们应该怎么办呢?
是的,你应该为这两个组件添加不同的键,这样 React 就会认为它们是不同的组件。componentDidMount 也会单独执行。
我们试试看:
复制 //...
// Pay attention here
name === 'fatfish' ? < Demo key = "1" name = { name } /> : <Demo key="2" name={ name } / >
//...
你也可以通过 CodePen 试试,https://codepen.io/qianlong/pen/NWywodV。