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

React父组件调用子函数组件affTimer方法报this is undefined错误

修复方案

1. 修正父组件ref创建方式

你在父组件构造函数中使用React.forwardRef()创建ref是错误的,forwardRef是用来包装子组件的API,创建ref应该使用React.createRef():

constructor(props) {
  super(props);
  this.state = {character: undefined};
  this.child = React.createRef(); // 替换原有代码
  this.startGame = this.startGame.bind(this);
}

2. 子组件暴露内部方法

函数组件默认不会将内部方法暴露给外部ref,需要配合forwardRef和useImperativeHandle手动暴露需要调用的affTimer方法。同时你代码中声明的定时器、计数变量都是普通JS变量,组件每次渲染都会重置,需要用useRef存储保证值的持久化:

// 新增引入依赖
import React, { forwardRef, useImperativeHandle, useRef, useEffect } from 'react';
import useTypingGame from "react-typing-game-hook";

const TypeF = forwardRef((props, ref) => {
  // 所有计数变量改用useRef存储
  const xpM = useRef(0);
  const i = useRef(0);
  const err = useRef(0);
  const xp = useRef(5);
  const tXP = useRef(0);
  const addXP = useRef(1);
  const bonus = useRef(0);
  const bonusCounter = useRef(0);
  const tmr = useRef(null);
  const cd = useRef(18);

  const text_array = [
    "There is strength and solidity within me",
    "Courage is flooding through my veins",
    "I possess strength within my heart",
    "I am leading the charge with courage, and a vigorous resolution",
    "There is a force inside me that is unbelievably powerful",
    "There is a brave, radiant spirit inside me",
    "I am a tall tree, with thick and strong roots",
    "I was born for this",
    "There is a divinity within",
    "I am a force of nature",
    "I possess the mental fortitude of those who climb the highest peaks",
    "I was born with a determined spirit",
    "There is an intensity in my eyes"
  ];
 
  const text = text_array[i.current];

  const {
    states: { charsState, length, currIndex, correctChar, errorChar },
    actions: { insertTyping, resetTyping, deleteTyping }
  } = useTypingGame(text);

  const handleKey = (key: any) => {
    if (key === "Escape") resetTyping();
    else if (key === "Backspace") deleteTyping(false);
    else if (key.length === 1) insertTyping(key);
  };

  if (currIndex + 1 === length) {
    xpM.current += 1;
    bonusCounter.current += 1;
    err.current += errorChar;
    addXP.current = ((xp.current * correctChar) - (err.current * 2)) * xpM.current;
    if (err.current > correctChar) addXP.current = correctChar * 3;
    tXP.current += addXP.current;
    if (bonusCounter.current >= 5) {
      bonus.current += 1;
      bonusCounter.current = 0;
    }
    resetTyping();
  }

  // 暴露affTimer方法给父组件ref
  useImperativeHandle(ref, () => ({
    affTimer: () => {
      tmr.current = setInterval(tock, 1000);
      if (i.current >= text_array.length) clearInterval(tmr.current);
    }
  }));

  const tock = () => {
    if (cd.current > 0) {
      cd.current -= 1;
      console.log(cd.current);
    } else if (i.current < text_array.length) {
      i.current += 1;
      cd.current = 18;
      resetTyping();
    }
  };

  // 组件卸载时清除定时器避免内存泄漏
  useEffect(() => {
    return () => tmr.current && clearInterval(tmr.current);
  }, []);

  return (
    <div className='container'>
      <div
        className="typing-test"
        id="start"
        onKeyDown={(e) => {
          handleKey(e.key);
          e.preventDefault();
        }}
        tabIndex={0}
      >
        {text.split("").map((char: string, index: number) => {
          const state = charsState[index];
          const color = state === 0 ? "white" : state === 1 ? "green" : "red";
          return (
            <span
              key={char + index}
              style={{ color }}
              className={currIndex + 1 === index ? "curr-letter" : ""}
            >
              {char}
            </span>
          );
        })}
      </div>
      <h2 className='debug'> TIMER: {cd.current}, I: {i.current}, ERRORS: {err.current},  MULTIPLIER: {xpM.current}, Type XP: {correctChar * xp.current}, CurrXP: {correctChar * xp.current * xpM.current} XPTotal: {tXP.current} bonusCounter: {bonusCounter.current}, BONUS: {bonus.current}</h2>
    </div>
  );
});

export default TypeF;

修改完成后父组件调用this.child.current.affTimer()就可以正常触发子组件的定时器方法,不会再报this is undefined的错误。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 11:24:00