React子组件向父组件传递累计重量值及实时渲染问题
问题分析与解决方案
问题背景
现有一个子组件,包含带重量属性的按钮,点击按钮可选择/取消选择对应载荷,所选载荷的重量会进行累加或扣除。需求是将子组件中计算出的累计重量sumCargoValue传递到父组件,并在父组件的计分板中展示。
当前遇到两个问题:
- 点击子组件按钮后,父组件的累计重量每两次点击才更新一次,但
localStorage中的记录是及时且正确的。 - 需要处理长按按钮后的正确渲染问题。
子组件代码
<ul className="lots__list"> ... {filter == "cargo" && lotsSplice.map((item) => ( <CargoButton key={item.id} id={item.id} weight_value={item.value} handleClick={buttonHandler} array={lotsSplice} card={item} ArraySaved={arraySaved} isAdded={item.isAdded} /> ))} </ul>
function buttonHandler(item, isAdded) { setLotsSplice( lotsSplice.map((elem) => elem.id === item.id ? { ...elem, isAdded: !elem.isAdded } : elem )); const tempCargoValue = sumCargoValue; setSumCargoValue(item.value * 1); if (isAdded) { setSumCargoValue(sumCargoValue * 1 + item.value * 1); } else { setSumCargoValue(sumCargoValue * 1 - item.value * 1); } onBookmarkClick(); }
父组件代码
function CheckWeightCargoFrame({ openPopup }) { ... const [sumCargoValue, setsumCargoValue] = useState(); const [BookmarkClick, setBookmarkClick] = useState(""); function onBookmarkClick() { setBookmarkClick(!setBookmarkClick); console.log(localStorage.getItem("selectCargo")); setsumCargoValue(localStorage.getItem("selectCargo")); } ... useEffect(() => { console.log(localStorage.getItem("selectCargo")); setsumCargoValue(localStorage.getItem("selectCargo")); } ); return ( ... <ListArray array={cargo} filter="cargo" onBookmarkClick={onBookmarkClick} ... /> )} <div className="check-weight__info"> {sumCargoValue} </div> ...
问题原因
- 子组件状态更新逻辑错误:
setSumCargoValue是异步操作,直接使用sumCargoValue计算会获取到旧的状态值。- 中间多余的
setSumCargoValue(item.value * 1);会覆盖后续的计算结果,导致状态更新混乱。
- 父组件回调函数错误:
setBookmarkClick(!setBookmarkClick);中误用了状态更新函数setBookmarkClick而非状态值BookmarkClick,导致状态切换逻辑失效。
- 依赖localStorage传递状态冗余且不可靠:
- 用
localStorage作为父子组件状态传递的中间层完全没必要,反而会因为localStorage的读写时机与React状态更新不同步,导致显示延迟。
- 用
- 无依赖的useEffect无限触发:
- 父组件中的
useEffect没有设置依赖项,会在组件每次渲染时都执行,干扰正常的状态更新流程。
- 父组件中的
解决方案
1. 修正子组件的重量计算逻辑
使用React的函数式状态更新,确保每次计算都基于最新的状态值,同时移除多余的状态更新语句:
function buttonHandler(item, isAdded) { // 更新选中状态 setLotsSplice(prev => prev.map(elem => elem.id === item.id ? { ...elem, isAdded: !elem.isAdded } : elem ) ); // 正确计算累计重量:基于前一次状态更新 setSumCargoValue(prevSum => { const newValue = Number(item.value); return isAdded ? prevSum + newValue : prevSum - newValue; }); // 直接把最新的sumCargoValue传给父组件,不用localStorage const newSum = isAdded ? sumCargoValue + Number(item.value) : sumCargoValue - Number(item.value); onBookmarkClick(newSum); }
2. 父组件直接接收子组件传递的重量值
移除冗余的localStorage操作和无依赖的useEffect,修正状态切换逻辑:
function CheckWeightCargoFrame({ openPopup }) { ... const [sumCargoValue, setsumCargoValue] = useState(0); // 初始化默认值为0 const [isBookmarked, setIsBookmarked] = useState(false); // 修正状态命名,更清晰 // 直接接收子组件传递的最新重量 function onBookmarkClick(newSum) { setIsBookmarked(prev => !prev); // 正确切换状态 setsumCargoValue(newSum); } // 移除无依赖的useEffect return ( ... <ListArray array={cargo} filter="cargo" onBookmarkClick={onBookmarkClick} initialSum={sumCargoValue} // 把父组件初始值传给子组件 ... /> )} <div className="check-weight__info"> {sumCargoValue} kg</div> ...
3. 处理长按按钮的渲染问题
在CargoButton组件中添加长按逻辑,使用useRef记录定时器,区分点击和长按:
function CargoButton({ id, weight_value, handleClick, isAdded }) { const longPressTimer = useRef(null); // 开始长按计时 const handleMouseDown = () => { longPressTimer.current = setTimeout(() => { // 长按逻辑:比如连续累加/扣除,这里根据需求调整 handleClick({ id, value: weight_value }, !isAdded); }, 500); // 长按阈值设为500ms,可自定义 }; // 取消长按计时,处理点击逻辑 const handleMouseUp = () => { if (longPressTimer.current) { clearTimeout(longPressTimer.current); // 普通点击逻辑 handleClick({ id, value: weight_value }, isAdded); } }; // 处理鼠标离开的情况 const handleMouseLeave = () => { if (longPressTimer.current) { clearTimeout(longPressTimer.current); } }; return ( <button className={`cargo-button ${isAdded ? 'active' : ''}`} onMouseDown={handleMouseDown} onMouseUp={handleMouseUp} onMouseLeave={handleMouseLeave} // 兼容移动端触摸事件 onTouchStart={handleMouseDown} onTouchEnd={handleMouseUp} onTouchCancel={handleMouseLeave} > {weight_value} kg </button> ); }
关键优化点总结
- 父子组件状态传递直接用回调函数,避免依赖
localStorage,保证状态同步。 - 使用函数式状态更新处理异步状态,确保计算基于最新值。
- 修正父组件中错误的状态切换逻辑,移除冗余的
useEffect。 - 通过定时器实现长按识别,区分普通点击和长按操作,保证渲染正确。
内容的提问来源于stack exchange,提问作者W300I
相关产品推荐
相关产品推荐

