React点击按钮递增计数并跳过3的倍数问题排查与解决
问题分析与解决
需求与问题现状
需求:实现默认从0开始,点击按钮递增计数,若当前计数的下一个值为3的倍数则跳过该值,直接显示下一个有效值,预期序列为0,1,2,4,5,7,8,10,11...
现有代码运行后实际序列为0,1,2,3,5,6,8,9...,未跳过3的倍数,反而跳过了3的倍数的下一个值,同时控制台打印的count总是滞后于UI显示的值。
问题原因
- 闭包捕获旧状态:React的
useState状态更新是异步批量处理的,changeCounter函数形成闭包,每次渲染时捕获的是当前周期的旧count值,这导致控制台打印的永远是上一次的状态,看起来滞后。async/await、setTimeout无法解决这个闭包陷阱,因为它们无法改变闭包捕获的旧值。 - 逻辑判断方向错误:当前逻辑是判断当前
count是否为3的倍数来决定增量,但需求是下一个值不能是3的倍数。比如当count=2时,下一个值是3(3的倍数),现有逻辑判断2%3!==0,执行setCount(3),导致UI显示3,违背预期。
修复方案
核心是基于下一个值判断是否需要跳过,同时使用函数式更新获取最新状态值,避免闭包陷阱。
修改后的代码如下:
import React, {useState} from "react"; export default function App() { const[count, setCount] = useState(0); const changeCounter = () => { // 函数式更新,c为当前最新状态值 setCount(c => { let next = c + 1; // 若下一个值是3的倍数,直接加1跳过 if (next % 3 === 0) { next += 1; } return next; }); } return ( <div className="App"> <h1>Hello People</h1> <p>Count: {count}</p> <button onClick={changeCounter}>Click me!</button> </div> ); }
代码说明
- 函数式更新:
setCount(c => ...)中的c是React提供的当前最新状态值,彻底避免闭包捕获旧值的问题,确保每次判断都基于最新的count。 - 逻辑调整:先计算下一个值
next = c + 1,如果next是3的倍数则再加1跳过,保证UI显示的永远不是3的倍数,完全符合预期序列。
若需要打印最新的count值,可通过useEffect监听状态变化:
import React, {useState, useEffect} from "react"; export default function App() { const[count, setCount] = useState(0); useEffect(() => { console.log('当前count值:', count); }, [count]); const changeCounter = () => { setCount(c => { let next = c + 1; if (next % 3 === 0) { next += 1; } return next; }); } return ( <div className="App"> <h1>Hello People</h1> <p>Count: {count}</p> <button onClick={changeCounter}>Click me!</button> </div> ); }
useEffect会在count更新后触发,打印的就是最新状态值,不会再出现滞后问题。
内容的提问来源于stack exchange,提问作者Rajat Sharma
相关产品推荐
相关产品推荐

