前言
不知不觉已经实习了2个月了,今天来和大家一起聊聊在实习过程中碰到的useState钩子。
在React的世界里,管理状态(state)是件大事。React提供了一个叫做useState的小工具,让我们可以在组件里轻松地使用状态。但有时候,我们可能会对状态更新的方式感到困惑。别担心,这篇文章会带你了解React是如何处理状态更新的,特别是一个叫做“批处理”的技巧,并通过一些简单的例子来说明。
什么是批处理
想象一下,你在餐厅点菜,你不会点一个菜就喊一次服务员,而是会等点完所有的菜再一起告诉服务员。React处理状态更新的方式有点像这样。当你在某个操作中多次更新状态时,React不会立刻一个一个地更新,而是把它们攒起来,等操作完成后一次性处理。这样可以节省时间,提高效率。
怎样连续更新状态
有时候,你可能需要在一个操作中多次更新同一个状态。React允许你这样做,并且会把所有的更新操作排好队,等当前操作完成后再一起处理。
各位看官请看以下代码:
import { useState } from 'react';
export default function Counter() {
const [number, setNumber] = useState(0);
return (
<>
<h1>{number}</h1>
<button onClick={() => {
setNumber(n => n + 1);
setNumber(n => n + 1);
setNumber(n => n + 1);
}}>加三次</button>
</>
)
}
在这个例子中,尽管我们三次点击了“加三次”按钮,React会在下一次渲染时一次性把计数器增加3。
更新函数和状态替换
有时候,你可能需要在更新状态后立即替换状态。React允许你传递一个更新函数给setNumber,这个函数会接收当前的状态作为参数,并返回新的状态。
<button onClick={() => {
setNumber(number + 5);
setNumber(n => n + 1);
}}>
增加数字
</button>
在这个例子中,尽管我们首先增加了5,然后又增加了1,但React会在下一次渲染时一次性处理,所以最终的状态值会增加6。
事件处理完成后的状态更新
React会在事件处理完成后处理状态更新。这意味着,如果你在事件处理中更新了状态,那么在处理完成之前,状态不会改变。
function App() {
const [count, setCount] = useState(0)
const [num, setNum] = useState(1)
async function handleClick() {
console.log('此时的count值', count);
setCount(count + 1)
console.log('第', num, '次点击');
setNum(num + 1)
console.log('第一次渲染:', count);
await delay()
setCount(count - 1)
console.log('第二次渲染:', count);
}
function delay() {
return new Promise(resolve => {
setTimeout(resolve, 1000)
})
}
return (
<>
{/* <p>hello world</p>
<Count></Count> */}
<button onClick={handleClick}>+1/-1</button>
{count}
</>
)
}
在这个例子中,我们模拟了一个异步操作,setPending首先增加1,然后等待3秒后减少1。
第一次点击(假设pending初始值为0):
count值变化过程:
- setCount(count + 1): count 是 0 所以 setCount(0 + 1)
- react准备在下一次渲染时将count更改为1
- setCount(count - 1): count是0 所以 setCount(0 - 1)
- react准备在下一次渲染时将count更改为-1
尽管你调用了多次 setCount,但在 这次渲染的 事件处理函数中 count 会一直是 0,所以你会交替将count加1(0 + 1)设置成1, 然后再减1(0 - 1)设置成-1。这就是为什么在你的事件处理函数执行完以后,React 重新渲染的组件中的 count 等于 -1而不是0 。
总结
通过理解React的批处理机制,我们可以更有效地管理状态更新,减少不必要的渲染,提高应用性能。在实际开发中,我们应该充分利用React提供的状态更新函数,确保状态的更新是可预测和一致的。
小挑战
相信到这里大家对react的useState批处理机制已经有了一定的了解,下面大家来一起看一个挑战题巩固一下。
现在,让我们来解决一个实际问题。假设我们正在开发一个艺术市场应用,用户可以为艺术品提交多个订单。我们需要确保“等待”和“完成”计数器的行为符合预期。
import { useState } from 'react';
export default function RequestTracker() {
const [pending, setPending] = useState(0);
const [completed, setCompleted] = useState(0);
async function handleClick() {
setPending(pending + 1);
await delay(3000);
setPending(pending - 1);
setCompleted(completed + 1);
}
return (
<>
<h3>
等待:{pending}
</h3>
<h3>
完成:{completed}
</h3>
<button onClick={handleClick}>
购买
</button>
</>
);
}
function delay(ms) {
return new Promise(resolve => {
setTimeout(resolve, ms);
});
}
在上述代码中,pending计数器可能会出现减少到-1的情况,这是因为在异步等待期间,pending状态可能被多次更新。为了解决这个问题,我们需要确保在异步操作期间,状态更新是正确的。
那么我们怎么来解决这个问题呢?我们可以通过使用更新函数来确保状态的正确更新。
async function handleClick() {
setPending(p => p + 1);
await delay(3000);
setPending(p => p - 1);
setCompleted(c => c + 1);
}
通过这种方式,我们可以确保即使在异步操作期间,状态的更新也是正确的,避免了出现负数的情况。
本篇文章到这里就结束了,希望这篇文章能帮助你更好地理解React的状态更新和批处理机制。
如果觉得本篇文章对你有所帮助,还请点赞+收藏+评论,谢谢大家。