React组件渲染两次(useEffect执行两次)及相关问题咨询
问题解决:React组件渲染两次、count显示异常及事件处理器错误
1. Strict Mode下组件/useEffect执行两次
React Strict Mode在开发环境下会刻意重复调用组件渲染和副作用函数(比如useEffect),目的是帮你排查潜在的副作用问题(比如非幂等操作),生产环境不会有这个行为。不需要移除Strict Mode,你的useEffect里已经通过判断商品是否存在避免了重复添加,逻辑是安全的,这个现象属于正常机制。
2. count显示为01而非1
问题出在count的初始值和更新方式:
- 你把count初始值设成了数组
[0],执行setCount(count + 1)时,数组和数字相加会被隐式转成字符串拼接,得到"01"。 - 直接用
count + 1还会有闭包问题,useEffect依赖的是prop.index,更新时拿到的count可能是旧值。
修正:
// 初始值改成数字0 const [count, setCount] = useState(0) // useEffect里用函数式更新获取最新值 setCount(prevCount => prevCount + 1)
3. 事件处理器报错且未点击就执行
未点击就触发函数
你直接写onClick={handlePlusMinus("-",prop.index)}会导致组件渲染时就调用函数,必须用箭头函数包裹,让点击时才执行:
<button onClick={() => handlePlusMinus("-", prop.index)}>-</button> <button onClick={() => handlePlusMinus("+", prop.index)}>+</button>
newarray[id][1]未定义报错
prop.index是商品在allItems里的索引,但productstobuy是按添加顺序存储的,两者索引不匹配,用prop.index去取productstobuy里的元素自然找不到。另外你减号逻辑写错了,应该是减1不是加1。
修正后的事件处理器(用商品本身作为标识查找,更可靠):
const handlePlusMinus = (sign, targetProduct) => { setProducts(prev => { return prev.map(product => { if (product[0] === targetProduct) { let newCount = product[1] if (sign === "+") { newCount += 1 } else if (newCount - 1 >= 1) { newCount -= 1 } return [product[0], newCount] } return product }) }) }
调用时传入当前商品:
{productstobuy.map((product) => { return ( <div className="product" key={product[0]}> {product[0]} <div> <button onClick={() => handlePlusMinus("-", product[0])}>-</button> <input type="number" value={product[1]} readOnly /> <button onClick={() => handlePlusMinus("+", product[0])}>+</button> </div> </div> ) })}
完整修正代码
import React, { useEffect, useState } from "react"; import { useOutletContext } from "react-router-dom"; function ShoppingCart(prop) { const [productstobuy, setProducts] = useState([]); const [count, setCount] = useState(0) const handlePlusMinus = (sign, targetProduct) => { setProducts(prev => { return prev.map(product => { if (product[0] === targetProduct) { let newCount = product[1] if (sign === "+") { newCount += 1 } else if (newCount - 1 >= 1) { newCount -= 1 } return [product[0], newCount] } return product }) }) } const allItems = useOutletContext() useEffect(() => { setCount(prevCount => prevCount + 1) const toadd = allItems[prop.index] const productExists = productstobuy.some(elem => elem[0] === toadd) if (!productExists) { setProducts(oldArray => [...oldArray, [toadd, 1]]); } }, [prop.index, allItems]) return ( <div> {count} {productstobuy.length > 0 ? productstobuy.map((product) => { return ( <div className="product" key={product[0]}> {product[0]} <div> <button onClick={() => handlePlusMinus("-", product[0])}>-</button> <input type="number" value={product[1]} readOnly /> <button onClick={() => handlePlusMinus("+", product[0])}>+</button> </div> </div> ) }) : null} </div> ); } export { ShoppingCart }
额外优化:
- 用
allItems[prop.index]代替filter,更高效 - 用
some判断商品是否存在,比filter更简洁 - 给map元素加key,符合React规范
- 把input设为只读,避免手动输入导致状态不一致
内容的提问来源于stack exchange,提问作者NoobWebDesigner
相关产品推荐
相关产品推荐

