你对 React 了解多少?看看大家常犯的错误

2025-06-08

你对 React 了解多少?看看大家常犯的错误

低垂的果实

根据我采访 React 开发人员的经验,有些东西是许多开发人员不知道的,但很容易学习,并且可以帮助他们成为更好的 React 开发人员,并处理怪癖和错误。

它们是关于如何使用 React 以及 React 如何工作的知识片段。

准备好了吗?以下是问题:

如果我们手动更改 DOM,React 在渲染应用程序时会考虑到这一点吗?

要回答这个问题,你需要了解虚拟 DOM 的工作原理。
简而言之,虚拟 DOM 是 DOM 树状态的表示,以纯 JavaScript 对象的形式呈现。
当 React 渲染我们的应用程序时,它会渲染新的虚拟 DOM,并将其与之前的虚拟 DOM进行比较。请注意,在这个阶段,它根本不会查看 DOM 的当前状态。

意思是 - 如果您手动更改 DOM,React 将忽略这些更改,并且/或者在重新渲染该元素时覆盖它们。

什么原因导致组件重新渲染?

很多人给出的答案是——要么是状态改变,要么是 props 改变,要么是 context 改变。
更正确的答案是——状态改变,要么是父组件重新渲染,要么是 context 改变。
默认情况下,如果父组件渲染,React 也会渲染所有子组件,即使之前的 props 完全相同。

属性的比较只发生在纯组件,或者实现了 Memo/shouldComponentUpdate 的组件中。

什么是功能组件?

出于某种原因,许多开发人员认为所有函数式组件都是纯粹的。事实并非如此。函数式组件过去是无状态的,但现在有了钩子,情况也发生了变化。因此,函数式组件和类组件之间的主要区别仅在于语法,并且只能在函数式组件内部使用钩子。
此外,有些操作只能在类组件中执行,例如定义 ErrorBoundaries。

组件名称必须以大写字母开头吗?

这是一个棘手的问题。虽然单个变量必须以大写字母开头才能作为组件,但如果使用嵌套属性,则也可以以小写字母开头。以下语法有效:

comps = { myComp: () => <div>hi</div> }
...
return <comps.myComp/>

状态是否异步更新?

这是一个非常棘手的问题,特别是因为 React 文档确实说它是异步的。

以此代码为例:

function MyComp() {
  console.log('render');
  const [counter, setCounter] = useState(0);

  const onClick = () => {
    setCounter(prev => prev + 1);
    console.log('after setCounter');
  }

  return <button onClick={onClick}>Click me</button>;
}

日志内容将是:'after setCounter', 'render'。
所以看起来渲染确实是在状态更新后异步发生的。但是,如果我们在 Promise 解析后添加一个异步日志会怎么样呢?

function MyComp() {
  console.log('render');
  const [counter, setCounter] = useState(0);

  const onClick = () => {
    Promise.resolve().then(() => console.log('promise'));
    setCounter(prev => prev + 1);
    console.log('after setCounter');
  }

  return <button onClick={onClick}>Click me</button>;
}

现在日志将会是:'after setCounter', 'render', 'promise'。这意味着渲染是同步的(它发生在 promise 被解决之前)。

那么到底发生了什么?

它感觉上是异步的,因为 React 以批处理的方式执行事件处理程序。这意味着渲染函数只会在事件处理程序完成后调用。所有状态更新都会在此之前排队。
因此,它可能感觉上是异步的,但它在事件处理程序完成后是同步发生的。

更复杂的是,在并发模式下,渲染可能真的是异步的。
所以 React 团队的建议是将状态更新视为始终异步的,我认为这是个好建议。

何时使用layoutEffect,何时使用effect?

布局效果很少受到关注,许多开发者并不完全理解它们。布局效果与效果的主要区别在于,布局效果在提交阶段(即实际 DOM 更改之后)同步发生,而效果是异步发生的。

那么什么时候应该使用布局效果呢?例如,当你想避免 UI 闪烁时。例如:

const ScrollSync = ({ scrollTop }) => {
  const container = useRef();
  useLayoutEffect(() => {
    container.current.scrollTop = scrollTop;
  }, [scrollTop]);
  return <div ref={container}>...</div>
}

我们使用布局效果将元素的滚动位置绑定到状态,以确保它同步发生并且不会造成任何延迟。

可以从事件处理程序保存对事件参数的引用吗?

答案是否定的。React

中的事件对象是可回收的。这意味着,将来,相同的事件引用可能会指向不同的事件。

如果必须这样做,请保存对事件或原生事件属性的引用,或者调用 event.persist() 将其从事件池中分离。

包起来

我希望你学到了一些新东西,或者至少对你已经知道的事情有了一些整理或新的看法。

如果您有任何反馈或我犯了错误 - 请在评论部分告诉我。

鏂囩珷鏉yu簮锛�https://dev.to/adamklein/how-knowledgable-you-are-about-react-see-common-mistakes-people-make-5ha4
PREV
招聘初级开发人员担任远程职位的公司列表 我们招聘远程初级开发人员
NEXT
Django 2024 年路线图