React按钮根据Redux状态变化更新disabled状态问题排查
问题诊断与修复
核心问题1:forEach无法终止函数导致判断失效
你的canAfford和meetsRequirements函数里用了forEach循环,但forEach内部的return false只会跳过当前迭代,不会终止整个函数——这意味着不管资源够不够、要求满足与否,这两个函数最后都会返回true,完全起不到判断作用。
修复方式:用every方法替代forEach。every会在所有元素满足条件时返回true,只要有一个不满足就立即返回false,正好匹配你的判断逻辑:
const canAfford = (): boolean => { console.log('Units:', units); console.log("Checking affordability"); if (resourceType.length !== price.length) { throw `BuyBtn Error: price length is ${price.length} but resource length is ${resourceType.length}.`; } // 所有资源都大于等于对应价格才返回true return resourceType.every((res, i) => { const resPrice = price[i]; return resources[res] >= resPrice; }); }; const meetsRequirements = (): boolean => { if (requirements.length === 0) { return true; } // 所有要求的科技都解锁才返回true return requirements.every((req) => tech[req]); };
核心问题2:普通变量无法触发组件重渲染
你用let disabled = false定义的是普通变量,即使在useEffect里修改它的值,也不会触发组件重新渲染——组件每次渲染时,disabled都会被重新初始化为false,useEffect里的修改根本不会体现在UI上。
修复方式:不需要额外的useState,直接在渲染阶段计算disabled状态即可。因为useSelector会在Redux状态变化时自动触发组件重渲染,此时重新计算disabled就能拿到最新值:
// 去掉原来的let disabled和useEffect,直接计算状态 const disabled = !(canAfford() && meetsRequirements());
核心问题3:错误的渲染条件导致按钮直接消失
你当前的逻辑是if (meetsRequirements() && canAfford())才渲染按钮,否则返回null。这意味着当资源不足或未满足要求时,按钮直接消失,而不是呈现禁用状态——完全违背了你的需求。
修复方式:去掉条件判断,始终渲染按钮,用disabled属性控制是否可点击:
return ( <button onClick={buyThing} disabled={disabled}>{label}</button> );
完整修复后的代码
function BuyBtn({ technology, label, resourceType, price, requirements = []}: IBuyBtn) { const units = useSelector((state: any) => state.units); const tech = useSelector((state: any) => state.tech); const resources = useSelector((state: any) => state.resources); const dispatch = useDispatch(); const canAfford = (): boolean => { console.log('Units:', units); console.log("Checking affordability"); if (resourceType.length !== price.length) { throw `BuyBtn Error: price length is ${price.length} but resource length is ${resourceType.length}.`; } return resourceType.every((res, i) => { const resPrice = price[i]; return resources[res] >= resPrice; }); }; const meetsRequirements = (): boolean => { if (requirements.length === 0) { return true; } return requirements.every((req) => tech[req]); }; const disabled = !(canAfford() && meetsRequirements()); const buyThing = () => { if (!disabled) { resourceType.forEach((res, i) => { const resPrice = price[i]; dispatch(SubtractResource(res, resPrice)); }); dispatch(UnlockTech(technology, true)) } }; return ( <button onClick={buyThing} disabled={disabled}>{label}</button> ); } export default BuyBtn;
额外优化建议
- 给
useSelector的state加上正确的类型定义,避免any类型,提升代码可读性和类型安全。 buyThing函数里直接用!disabled判断即可,无需重复调用canAfford和meetsRequirements,减少不必要的计算。
内容的提问来源于stack exchange,提问作者Eric
相关产品推荐
相关产品推荐

