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
相关产品推荐
相关产品推荐

