React中从输入框向有序列表添加项时,重新加载后首个列表项重复添加的原因排查
问题原因及修复方案
看起来你遇到的重复添加问题主要有两个核心原因,结合React的渲染机制和你的代码写法导致的,咱们一步步拆解:
1. 错误使用普通变量维护状态,而非React State
你在组件内部声明了const listItems = [],这是一个普通的组件内变量,不是React的状态。每次组件重新渲染时(包括React严格模式下的双重渲染,这在开发环境默认开启),这个变量都会被重新初始化为空数组。而你的clearAddUpdateState函数里,先向这个数组push值,再用它来更新state:
listItems.push(inputRef.current.value) setItems(prevList => prevList.concat(listItems))
在开发环境的严格模式中,React会故意执行两次组件渲染和更新逻辑来检测副作用,这就导致第一次点击"Add"时,listItems.push被执行了两次,最终concat的时候就把两个相同的值添加到了状态里,出现重复项。
另外,普通变量不会触发组件重新渲染,也不受React状态管理的控制,完全不应该用来维护需要渲染的列表数据。
2. 不稳定的key值(次要但需修复)
你用Math.random()作为<li>的key,这会导致每次组件渲染时,列表项的key都发生变化,React无法正确跟踪列表项的身份,可能引发不必要的重新渲染,甚至在某些场景下导致渲染错误,虽然这不是当前重复问题的直接原因,但也是需要修正的问题。
修复后的代码
我们完全用React的useState来管理列表状态,移除多余的普通变量,同时改用稳定的key:
import { useRef, useState } from 'react' export function GroceryAllin() { const inputRef = useRef(null) // 直接初始化空数组作为初始状态,不需要额外的listItems变量 const [items, setItems] = useState([]) function removeLastItem() { setItems(prevList => prevList.slice(0, prevList.length - 1)) } function clearAddUpdateState() { const newItem = inputRef.current.value.trim() // 先判断输入是否为空,避免添加空项 if (!newItem) return // 直接基于之前的状态更新,不需要依赖外部数组 setItems(prevList => [...prevList, newItem]) inputRef.current.value = "" } return ( <div className="container"> <label htmlFor="itemInput">Add a/an : </label> <input ref={inputRef} type="text" name="itemInput" id="itemInput" /> <button onClick={clearAddUpdateState} type="button">Add</button> {/* 用索引作为key(如果列表项不会被重新排序的话可以用,更好的方式是给每个项生成唯一id) */} <ol>{items.map((el, index) => <li key={index}>{el}</li>)}</ol> <button onClick={removeLastItem} type="button">Remove last </button> </div> ) }
关键修改点说明:
- 移除了
listItems普通变量,直接用useState([])初始化列表状态,所有状态更新都通过setItems的回调函数,基于之前的状态(prevList)来更新,确保每次状态更新都是可靠的。 - 添加了输入内容的空值判断,避免添加空的列表项。
- 将
prevList.concat(listItems)改为[...prevList, newItem],直接把新项添加到之前的列表里,逻辑更清晰,也不需要依赖外部数组。 - 改用列表项的索引作为key(如果你的列表涉及排序、删除中间项等操作,建议用更稳定的唯一id,比如给每个新项生成一个时间戳:
key={Date.now() + index},或者使用uuid库生成唯一标识)。
这样修改后,就不会出现第一次添加项重复的问题了,同时代码也更符合React的状态管理规范。
内容的提问来源于stack exchange,提问作者Bepis1446
相关产品推荐
相关产品推荐

