react 函数式更新数据的优势

114 阅读2分钟

下面代码,你习惯用哪种?

const handleClick = () => { 
    setIsOpen((prev) => !prev); 
}; 

const handleClick = () => { 
    setIsOpen(!isOpen); 
}; 

这两种写法其实在功能上是等效的,但它们在 语义清晰度性能考虑 上有一些区别。以下是对这两种写法的分析:

1. 使用 setIsOpen((prev) => !prev);

const handleClick = () => {
    setIsOpen((prev) => !prev);
};

优点:

  • 函数式更新setIsOpen((prev) => !prev) 使用了函数式更新的方式,这种方式基于当前的状态值(prev)来计算并更新状态。它有一个非常重要的优势:
    • 保证状态更新是基于最新的状态:如果你在多个地方或异步操作中修改状态,React 会将每个状态更新排队处理,确保使用的始终是最新的状态。这样,即使你快速连续调用 setIsOpen,也能确保每次都基于上一次的状态进行更新。

适用场景:

  • 当你的状态依赖于前一个状态值时,尤其是在异步操作或多个快速连续更新的情况下,使用这种方式能确保安全更新。

性能:

  • 在大多数情况下,React 会做性能优化,因此使用这种方式不会引入显著的性能损耗。

2. 使用 setIsOpen(!isOpen);

const handleClick = () => {
    setIsOpen(!isOpen);
};

优点:

  • 简洁易懂:这种写法在大多数情况下足够简洁,直接基于当前的状态值(isOpen)来更新状态,适用于简单的场景。

缺点:

  • 潜在的同步问题:当你使用这种方式时,isOpen 的值可能并不是最新的。React 的 setState 是异步更新的,如果你在同一个事件处理器中多次调用 setIsOpen(!isOpen),可能会遇到因为状态更新的延迟而导致的不一致问题(即你更新的是过时的 isOpen 值)。

适用场景:

  • 如果你的状态更新并不依赖于前一个状态,并且不会有快速连续的状态更新,使用这种写法是没问题的。

性能:

  • 和前一种写法一样,这种写法不会带来额外的性能问题,除非在连续快速的事件触发下(例如在循环中),这种方式可能导致状态更新出错。

总结:

  • 推荐使用 setIsOpen((prev) => !prev),因为它更安全,尤其是在处理异步操作或多个快速更新时,能够保证基于最新的状态更新。

  • setIsOpen(!isOpen) 在简单场景下也可以使用,但需要小心状态更新的时序问题。如果你的状态更新是独立的,且不依赖于前一个状态的值,这种写法就足够简洁了。

最佳实践:

  • 函数式更新:当状态依赖于其前一个状态时,推荐使用 setIsOpen((prev) => !prev),这种方法更加稳妥。