React开发中五个常见错误及解决方法

摘要:很多React开发者在工作中会遇到一些看似简单却影响深远的问题。今天我们来聊聊五个常见的React错误,了解它们产生的原因和正确的做法。

很多React开发者在工作中会遇到一些看似简单却影响深远的问题。今天我们来聊聊五个常见的React错误,了解它们产生的原因和正确的做法。


1. 内联使用React.memo的问题

当我们直接使用箭头函数创建被memo包裹的组件时,在React性能分析器中会显示为"Anonymous",这让我们很难识别是哪个组件在重复渲染。

import { memo } from 'react'

// 在性能分析器中显示为'Anonymous'
export const MyComponent = memo((props) => {
  return <div>组件内容</div>
})

更好的做法是先声明组件,再用memo包裹:

const MyComponentInternal = (props) => {
  return <div>组件内容</div>
}

// 在性能分析器中显示为'MyComponent'
export const MyComponent = memo(MyComponentInternal)

需要注意的是,不当使用memo反而会降低性能。因为每次渲染时比较props需要消耗资源,如果节省的渲染次数不足以抵消这个消耗,使用memo就没有意义。


2. 路由中使用相同的错误边界

在多个路由页面中使用相同的错误边界组件时,会出现一个问题:当用户从一个出错页面导航到另一个页面时,错误界面不会消失。

<Route path="books" element={
  <ErrorBoundary>
    <Books/>
  </ErrorBoundary>
}/>

<Route path="users" element={
  <ErrorBoundary>
    <Users/>
  </ErrorBoundary>
}/>

这是因为React认为这些错误边界是同一个组件,在导航时只是更新而不是重新挂载。

解决方法是为每个错误边界添加唯一的key:

<Route path="books" element={
  <ErrorBoundary key="books">
    <Books/>
  </ErrorBoundary>
}/>

<Route path="users" element={
  <ErrorBoundary key="users">
    <Users/>
  </ErrorBoundary>
}/>

或者创建自定义的错误边界组件:

const PageErrorBoundary = ({ children }) => {
  const location = useLocation()
  return <ErrorBoundary key={location.pathname}>{children}</ErrorBoundary>
}


3. 错误使用.sort方法

JavaScript的.sort方法会直接修改原数组,这在React中可能引发问题。

const items = [3, 1, 2]

// 这会直接修改原数组
items.sort((a, b) => a - b)

console.log(items) // [1, 2, 3]

如果在state上使用.sort,其他依赖原数组顺序的组件就会出现问题。而且由于引用没有改变,使用setItems(items.sort())可能不会触发重新渲染。

正确的做法是创建新数组:

// 方法1:使用展开运算符
const sortedItems = [...items].sort((a, b) => a - b)

// 方法2:使用toSorted(较新浏览器)
const sortedItems = items.toSorted((a, b) => a - b)

// 方法3:在map或filter之后使用sort是可以的
const processedItems = items
  .filter(item => item.active)
  .sort((a, b) => a.name.localeCompare(b.name))


4. 使用0作为条件判断

在JavaScript中,我们经常用数组长度作为条件判断:

// 当items.length为0时,会显示数字0
items.length && <List items={items} />

这是因为React会渲染数字0,导致界面上出现一个多余的"0"。

应该使用明确的比较:

// 正确做法1
items.length > 0 && <List items={items} />

// 正确做法2
!!items.length && <List items={items} />

// 正确做法3
items.length ? <List items={items} /> : null


5. useEffect和useLayoutEffect的选择

useEffect和useLayoutEffect的执行时机不同:

  • useEffect:在DOM更新后异步执行,用户可能看到中间状态

  • useLayoutEffect:在DOM更新后同步执行,确保用户看到最终状态

// 可能导致闪烁
useEffect(() => {
  // 副作用代码
}, [dependency])

// 避免闪烁
useLayoutEffect(() => {
  // 需要同步执行的副作用
}, [dependency])

使用建议:

  • 涉及DOM测量和同步更新的操作使用useLayoutEffect

  • 数据获取、事件监听等异步操作使用useEffect

  • 服务端渲染时注意useLayoutEffect会警告,需要特殊处理


额外建议:key属性的正确使用

在处理列表时,很多开发者会使用索引作为key,这可能导致问题:

// 不推荐
{items.map((item, index) => (
  <div key={index}>{item.name}</div>
))}

// 推荐使用唯一ID
{items.map(item => (
  <div key={item.id}>{item.name}</div>
))}

当列表可能重新排序时,使用索引作为key会导致组件状态混乱。


总结

这些React开发中的常见错误看似简单,但对应用性能和用户体验影响很大。通过理解这些问题的根源并采用正确的解决方案,我们可以写出更健壮、更高效的React代码。

记住,好的React开发不仅仅是让代码工作,还要考虑性能、可维护性和用户体验。希望这些建议能帮助你在React开发中避免这些陷阱。

本文内容仅供个人学习、研究或参考使用,不构成任何形式的决策建议、专业指导或法律依据。未经授权,禁止任何单位或个人以商业售卖、虚假宣传、侵权传播等非学习研究目的使用本文内容。如需分享或转载,请保留原文来源信息,不得篡改、删减内容或侵犯相关权益。感谢您的理解与支持!

链接: https://shenqiku.cn/article/FLY_12991