React状态函数useState为何导致counter变量无法正常递增?
React Todo组件中counter变量重置的原因及修复方案
问题核心原因
你遇到的counter前两次点击都输出0的问题,本质是普通变量与React状态的生命周期差异:
- 你定义的
counter是组件函数内部的普通变量,只要组件触发重渲染(比如调用Settodo更新状态后),整个Todo组件函数会重新执行一遍,var counter = 0这条语句也会再次运行,直接把counter重置为0。 - 第一次点击ADD按钮时,先打印counter(此时是0),随后调用
Settodo更新状态,React触发组件重渲染,counter被重新设为0;第二次点击时,counter又从0开始计数,所以前两次输出都是0。而移除Settodo后,组件不会触发重渲染,counter一直是同一个变量,自然能正常递增。
另外你的代码还存在不符合React设计理念的问题:直接手动操作DOM创建、添加元素,React的核心是用状态驱动视图,应该通过状态管理列表内容,而非手动操作DOM。
修复方案
1. 用useState管理counter和待办列表
把counter和待办列表都改成React状态变量,让React帮你保留状态值,避免重渲染时丢失:
import React, { useState } from 'react' export default function Todo() { // 用useState管理待办列表数组 const [todoList, setTodoList] = useState([]) // 用useState管理计数器状态 const [counter, setCounter] = useState(0) const addTodo = () => { const input = document.getElementById('input') const inputVal = input.value.trim() // 空内容不添加 if (!inputVal) return console.log(counter) // 更新待办列表:基于旧状态生成新数组 setTodoList(prevList => [...prevList, { id: `list-val-${counter}`, text: inputVal }]) // 更新计数器:基于旧值递增 setCounter(prevCount => prevCount + 1) // 清空输入框 input.value = '' } return ( <> <center> <h1>Todo</h1> <input id="input" type="text" /> <button onClick={addTodo}>ADD</button> <div id="Todo-list"> {/* 用React渲染列表,自动处理DOM更新 */} {todoList.map(item => ( <li key={item.id} id={item.id}>{item.text}</li> ))} </div> </center> </> ) }
2. 关键改动说明
- 用
useState管理counter,状态值会在组件重渲染时被React保留,不会被重置。 - 用数组
todoList管理所有待办项,通过map方法渲染列表,完全遵循React状态驱动视图的逻辑,避免手动操作DOM带来的混乱。 - 更新状态时使用函数式更新(比如
setCounter(prevCount => prevCount + 1)),确保每次状态更新都基于最新的旧值,避免异步更新导致的错误。
内容的提问来源于stack exchange,提问作者Nikesh
相关产品推荐
相关产品推荐

