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

React报错Uncaught TypeError: x.method() is not a function的解决方案咨询

修复player1.setSpell is not a function错误的方案

这个报错的核心原因是:你存在useState里的player1并不是真正的Player类组件实例,而是React元素(也就是<Player />JSX编译后生成的普通对象,由React.createElement返回),这类对象并不包含你在类组件里定义的setSpell实例方法。

下面给你两种可行的修复方案,你可以根据自己的场景选择:

方案一:使用useRef获取类组件实例

React允许通过ref来获取类组件的真实实例,这是直接调用类组件实例方法的正确方式:

  1. 修改Match组件,用useRef替代useState来存储Player实例:
function Match(props) {
  const player1Ref = useRef(null);
  const player2Ref = useRef(null);
  const [isOn, setIsOn] = useState(false);
  const [turnCount, setTurnCount] = useState(0);

  useEffect(() => {
    const spell_archive = ['carmelo dragon','frog knight','thunder cloud','poison breath','ooze','refrigerator ice cube soldier'];
    if (isOn) {
      // 用函数式更新获取最新的turnCount,避免闭包问题
      setTurnCount(prevCount => prevCount + 1);
      //draw phase
      if (turnCount === 1){
        // 通过ref.current获取真实的Player实例
        player1Ref.current.setSpell(randomSpell(spell_archive));
      }
      setIsOn(false);
    }
  }, [isOn, turnCount]);

  // 在渲染Player组件时绑定ref
  return (
    <div>
      <Player ref={player1Ref} name={props.p1.name} />
      <Player ref={player2Ref} name={props.p2.name} />
      {/* 其他渲染内容 */}
    </div>
  )
}
  1. 你的Player类组件默认支持被ref引用,不需要额外修改。

方案二:状态提升(更符合React数据流理念)

React推荐单向数据流,把组件的状态放到共同的父组件中管理,Player组件作为受控组件接收状态和更新方法,这种方式比直接调用实例方法更符合React的设计模式:

  1. 修改Player组件为受控组件,移除内部的spells状态,通过props接收:
class Player extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      name: props.name || 'Tom Default',
      speed: 0,
      power: 0,
      health: 100,
    };
  }

  // 可选:保留setSpell作为props方法的包装,方便组件内部调用
  // setSpell = (spell) => {
  //   this.props.onAddSpell(spell);
  // }
}
  1. 在Match组件中管理两个玩家的spells状态,并传递给Player组件:
function Match(props) {
  const [player1Spells, setPlayer1Spells] = useState([]);
  const [player2Spells, setPlayer2Spells] = useState([]);
  const [isOn, setIsOn] = useState(false);
  const [turnCount, setTurnCount] = useState(0);

  useEffect(() => {
    const spell_archive = ['carmelo dragon','frog knight','thunder cloud','poison breath','ooze','refrigerator ice cube soldier'];
    if (isOn) {
      setTurnCount(prevCount => prevCount + 1);
      if (turnCount === 1){
        const newSpell = randomSpell(spell_archive);
        // 更新player1的spells状态
        setPlayer1Spells(prev => [...prev, newSpell]);
      }
      setIsOn(false);
    }
  }, [isOn, turnCount]);

  return (
    <div>
      <Player 
        name={props.p1.name} 
        spells={player1Spells} 
        // 如果Player需要内部添加法术,传递这个方法
        onAddSpell={(spell) => setPlayer1Spells(prev => [...prev, spell])}
      />
      <Player 
        name={props.p2.name} 
        spells={player2Spells} 
        onAddSpell={(spell) => setPlayer2Spells(prev => [...prev, spell])}
      />
    </div>
  )
}

额外说明

  • 方案一更适合快速解决现有代码的问题,但直接操作组件实例会打破React的数据流,不利于后续维护。
  • 方案二更符合React的设计原则,状态统一管理,组件之间的依赖更清晰,推荐在长期项目中使用。

内容的提问来源于stack exchange,提问作者sussiete

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.27 13:28:17