You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

使用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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.21 07:45:31