React+TypeScript角色选择器切换种族时状态滞后问题
问题:React + TypeScript角色选择器切换种族时UI滞后一步
使用React + TypeScript开发简易角色选择器,从data.json获取种族数组并存入状态,通过“Increase”和“Decrease”按钮切换种族,每个种族带有属性加成。但点击按钮时,显示的种族总是滞后一步:首次点击“下一个”/“上一个”,UI不会立即更新,需第二次点击才会变更,仅循环逻辑看似生效。
组件简化代码
type BaseAttributesType = { strength?: number; dexterity?: number; intelligence?: number; constitution?: number; charisma?: number; wisdom?: number; }; type RacesType = { name: string; description: string; bonuses: BaseAttributesType; img: string; }; const App = () => { const [baseAttributes, setBaseAttributes] = useState<BaseAttributesType | null>(null); const [races, setRaces] = useState<RacesType[] | null>(null); const [race, setRace] = useState<RacesType | null>(null); const [index, setIndex] = useState<number>(0); useEffect(() => { fetch("data.json") .then(res => res.json()) .then(data => { setBaseAttributes(data.base_attributes); setRaces(data.races); setRace(data.races[0]); }); }, []); const Increase = () => { if (races && index === races.length - 1) { setIndex(0); } else { setIndex(index + 1); } if (races) { setRace(races[index]); } }; const Decrease = () => { if (races && index === 0) { setIndex(races.length - 1); } else { setIndex(index - 1); } if (races) { setRace(races[index]); } }; return ( <div className="container"> <CharacterPreview img={race?.img || ""}></CharacterPreview> <div className="sor"> <img onClick={Decrease} id="balos" src="img/arrow.png" alt="" /> <h2>{race?.name}</h2> <img id="jobbos" onClick={Increase} src="img/arrow.png" alt="" /> </div> <span>Erő</span> <StatBar value={(baseAttributes?.strength || 0)+(race?.bonuses.strength || 0)}></StatBar> <span>Ügyesség</span> <StatBar value={(baseAttributes?.dexterity || 0)+(race?.bonuses.dexterity || 0)}></StatBar> <span>Intelligencia</span> <StatBar value={(baseAttributes?.intelligence || 0)+(race?.bonuses.intelligence || 0)}></StatBar> <span>Állóképesség</span> <StatBar value={(baseAttributes?.constitution || 0)+(race?.bonuses.constitution || 0)}></StatBar> <span>Karizma</span> <StatBar value={(baseAttributes?.charisma || 0)+(race?.bonuses.charisma || 0)}></StatBar> <span>Bölcsesség</span> <StatBar value={(baseAttributes?.wisdom || 0)+(race?.bonuses.wisdom || 0)}></StatBar> </div> ); }; export default App;
问题原因
React的setState是异步更新状态的。在Increase和Decrease函数中,调用setIndex后立刻使用当前的index值去设置race,此时index还未完成更新,所以race拿到的是旧索引对应的种族,导致UI滞后一步。
解决方案
方案1:通过index推导当前种族(推荐)
删除单独维护的race状态,直接通过index从races数组中推导当前种族,避免状态不同步:
const App = () => { const [baseAttributes, setBaseAttributes] = useState<BaseAttributesType | null>(null); const [races, setRaces] = useState<RacesType[] | null>(null); const [index, setIndex] = useState<number>(0); // 直接通过索引推导当前种族 const currentRace = races ? races[index] : null; useEffect(() => { fetch("data.json") .then(res => res.json()) .then(data => { setBaseAttributes(data.base_attributes); setRaces(data.races); }); }, []); const Increase = () => { if (!races) return; // 使用函数式更新确保拿到最新的索引值 setIndex(prevIndex => prevIndex === races.length - 1 ? 0 : prevIndex + 1); }; const Decrease = () => { if (!races) return; setIndex(prevIndex => prevIndex === 0 ? races.length - 1 : prevIndex - 1); }; return ( <div className="container"> <CharacterPreview img={currentRace?.img || ""}></CharacterPreview> <div className="sor"> <img onClick={Decrease} id="balos" src="img/arrow.png" alt="" /> <h2>{currentRace?.name}</h2> <img id="jobbos" onClick={Increase} src="img/arrow.png" alt="" /> </div> <span>Erő</span> <StatBar value={(baseAttributes?.strength || 0)+(currentRace?.bonuses.strength || 0)}></StatBar> <span>Ügyesség</span> <StatBar value={(baseAttributes?.dexterity || 0)+(currentRace?.bonuses.dexterity || 0)}></StatBar> <span>Intelligencia</span> <StatBar value={(baseAttributes?.intelligence || 0)+(currentRace?.bonuses.intelligence || 0)}></StatBar> <span>Állóképesség</span> <StatBar value={(baseAttributes?.constitution || 0)+(currentRace?.bonuses.constitution || 0)}></StatBar> <span>Karizma</span> <StatBar value={(baseAttributes?.charisma || 0)+(currentRace?.bonuses.charisma || 0)}></StatBar> <span>Bölcsesség</span> <StatBar value={(baseAttributes?.wisdom || 0)+(currentRace?.bonuses.wisdom || 0)}></StatBar> </div> ); };
方案2:预先计算新索引同步更新
在Increase和Decrease中先算出新索引值,再同步更新index和race:
const Increase = () => { if (!races) return; const newIndex = index === races.length - 1 ? 0 : index + 1; setIndex(newIndex); setRace(races[newIndex]); }; const Decrease = () => { if (!races) return; const newIndex = index === 0 ? races.length - 1 : index - 1; setIndex(newIndex); setRace(races[newIndex]); };
预期效果
点击“下一个”会立即切换到下一个种族,最后一个种族点击后切换到第一个;点击“上一个”反向循环,UI实时更新无滞后。
内容的提问来源于stack exchange,提问作者Bundáskenyér
相关产品推荐
相关产品推荐

