使用useState钩子无法向数组添加多个对象的问题排查
问题原因与解决方案
核心问题:闭包导致的状态快照过期
你的updatePlayer是异步函数,当它执行到setPlayers时,捕获到的players状态是函数创建时的旧快照(比如初始空数组),所以每次更新都是基于旧数组添加新对象,而非最新的数组,最终只会保留最后一次添加的对象。
解决步骤:
1. 使用函数式更新状态
将setPlayers改为函数式更新,直接基于上一次的状态进行修改,彻底避免闭包问题:
setPlayers(prevPlayers => [...prevPlayers, newPlayerObject])
2. 修正useEffect的依赖关系
updatePlayer依赖外部状态和变量,需确保useEffect能正确追踪这些依赖,防止出现无效的闭包:
- 方法一:用
useCallback包裹updatePlayer并添加到依赖数组
import { useCallback } from 'react'; const updatePlayer = useCallback(async () => { const provider = new ethers.providers.Web3Provider(window.ethereum) const contract = new ethers.Contract(stallionRunAddress, abi, provider) const playerIndex = playersEntered - 1 const newIndex = playerIndex.toString() const newPlayer = await contract.getPlayerAddress(newIndex) const newPlayerLevelBI = await contract.ownedHorseLevel(newPlayer) const newPlayerLevel = parseInt(newPlayerLevelBI) const newPlayerHorseArr = await contract.ownedHorseName(newPlayer) const newPlayerHorse = newPlayerHorseArr.toString() const newPlayerObject = {address: newPlayer, level: newPlayerLevel, horse: newPlayerHorse} setPlayers(prevPlayers => [...prevPlayers, newPlayerObject]) }, [stallionRunAddress, abi, playersEntered]) // 添加上所有外部依赖 useEffect(() => { if(playersEntered > 0) { updatePlayer() } }, [playersEntered, updatePlayer])
- 方法二:将
updatePlayer直接定义在useEffect内部
useEffect(() => { const updatePlayer = async () => { const provider = new ethers.providers.Web3Provider(window.ethereum) const contract = new ethers.Contract(stallionRunAddress, abi, provider) const playerIndex = playersEntered - 1 const newIndex = playerIndex.toString() const newPlayer = await contract.getPlayerAddress(newIndex) const newPlayerLevelBI = await contract.ownedHorseLevel(newPlayer) const newPlayerLevel = parseInt(newPlayerLevelBI) const newPlayerHorseArr = await contract.ownedHorseName(newPlayer) const newPlayerHorse = newPlayerHorseArr.toString() const newPlayerObject = {address: newPlayer, level: newPlayerLevel, horse: newPlayerHorse} setPlayers(prevPlayers => [...prevPlayers, newPlayerObject]) } if(playersEntered > 0) { updatePlayer() } }, [playersEntered, stallionRunAddress, abi]) // 添加上所有外部依赖
3. 确保playersEntered的更新逻辑正确
确认每次有新玩家加入时,playersEntered是通过函数式更新递增的,避免被意外重置:
// 正确的递增方式 setPlayersEntered(prev => prev + 1)
内容的提问来源于stack exchange,提问作者Umair
相关产品推荐
相关产品推荐

