在通过父函数调用子函数时未定义



更新:我在typescript文件中找到了一个更简单的解决方案,因此不再需要JS父类。~捂脸~谢谢你的建议!

我正试图获得一个按钮来触发子函数组件内的函数affTimer(),但我不断得到错误"这是未定义的";与函数调用相关。下面是两个代码文件:

affType.js

import React, {Component}  from 'react';
import ReactPlayer from 'react-player'
import { Link } from 'react-router-dom';
import affirmationService from '../Services/requestService'
import affTrack from '../audio/inner.wav';
import warn from '../audio/warning.wav';
import Player from '../Player/Player';
import videoBG from '../videos/InnerStrength.mp4';
import Type from '../Type/Type.tsx';
import Button from "../customButton";
import {tXP} from '../Type/Type.tsx';
class affType extends Component {
constructor(props) {
super(props);
this.state = {character: undefined};
this.child = React.forwardRef();
this.startGame = this.startGame.bind(this);

}

async componentDidMount() {
const { match: { params } } = this.props;
//let affirmation_id = params.affirmation_id;
//let response = await affirmationService.getById(affirmation_id);
//this.setState({character: response.data});
setTimeout(() => {
document.getElementById('overlay_blk_fast').style.opacity = 0;
setTimeout(() => {
document.getElementById('overlay_blk_fast').style.display = 'none';
}, 1000);
}, 10);
}

spawnDialog() {
document.getElementById('overlay_1').style.display = 'block';
setTimeout(() => {
document.getElementById('overlay_1').style.opacity = 1;
}, 10);
}
destroyDialog() {
document.getElementById('overlay_1').style.opacity = 0;
setTimeout(() => {
document.getElementById('overlay_1').style.display = 'none';
}, 1000);
}
repeat() {
document.getElementById('overlay_2').style.opacity = 0;
document.querySelector('video').play();
setTimeout(() => {
document.getElementById('overlay_2').style.display = 'none';
}, 1000);
}
test_ended() {
document.getElementById('overlay_2').style.display = 'block';
setTimeout(() => {
document.getElementById('audio_end').play();
document.getElementById('overlay_2').style.opacity = 1;
}, 10);
}
startGame() {
var track = document.getElementById('aff');
track.play();
this.child.current.affTimer();
}
render() {

return ( 
<div>
<div className="contentplayer">
<audio id='aff'><source src={affTrack} /></audio>
<video autoPlay muted loop id="myVideo">
<source src={videoBG} type="video/mp4" />
</video>      
<audio id="audio_end" src="/Audio/Inner Strength completed quest - play with completed quest prompt.wav"/>
</div>
<p>{tXP}</p>
<Button 
border="none"
color="pink"
height = "200px"
onClick={this.startGame}
radius = "50%"
width = "200px"
children = "Start!"
/>
<Type ref={this.child}> 

</Type>
<div className="aligntopright" onClick={() => {this.spawnDialog()}}>
<div className="backbtn-white"></div>
</div>

<div className="overlay_blk_fast" id="overlay_blk_fast"></div>
<div className="overlay" id="overlay_1">
<div className="dialog">
<div className="dialogcontainer">
<img className="dialogbg"/>
<h3 className="dialogtext">Are you sure you would like to go back to the selection page?</h3>
<h2 className="no" onClick={() => {this.destroyDialog()}}>No</h2>
<Link to="/affirmation"><h2 className="yes">Yes</h2></Link>
</div>
</div>
</div>
<div className="overlay" id="overlay_2">
<div className="dialog">
<div className="dialogcontainer">
<img className="dialogbg"/>
<h3 className="dialogtext">Would you like to repeat this quest?</h3>
<Link to="/affirmation"><h2 className="no">Go back</h2></Link>
<h2 className="yes" onClick={() => {this.repeat()}}>Repeat</h2>
</div>
</div>
</div>
</div>
)
}
}
export default affType;

type.tsx

import React, {Component}  from 'react';
import useTypingGame from "react-typing-game-hook";
import { textSpanContainsTextSpan } from 'typescript';
var xpM = 0;
var i = 0;
var err = 0;
var xp = 5;
var tXP = 0;
var addXP = 1;
var bonus = 0;
var bonusCounter = 0;
//var warnP = new Audio({warn});
//var affTrackP = new Audio('../audio/inner.wav');
function TypeF() {

let 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"
];

let text = text_array[i];
const {
states: {
charsState,
length,
currIndex,
currChar,
correctChar,
errorChar,
phase,
startTime,
endTime
},
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 = xpM + 1;
bonusCounter = bonusCounter + 1;
err = err + errorChar;
addXP = ((xp * correctChar) - (err * 2)) * xpM;
if (err > correctChar) {
addXP = correctChar * 3;
}
tXP = tXP + addXP;
if (bonusCounter >= 5) {
bonus = bonus + 1;
bonusCounter = 0;
}
resetTyping();
}

var tmr;
var cd = 18;
function affTimer() {
tmr = setInterval(tock, 1000);
if (i >= text_array.length) {
clearInterval(tmr);
}
}  
function tock() {
if (cd > 0) {
cd = cd - 1;
console.log(cd);
}
else if (cd <= 0) {
if (i < text_array.length) {
i = i + 1;
cd = 18;
resetTyping();
}
else {
i = text_array.length;
}
}
}

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) => {
let state = charsState[index];
let 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}, I: {i}, ERRORS: {err},  MULTIPLIER: {xpM}, Type XP: {correctChar * xp}, CurrXP: {correctChar * xp * xpM} XPTotal: {tXP} bonusCounter: {bonusCounter}, BONUS: {bonus}</h2>
</div>
);
}
export {tXP};
export default TypeF;

任何帮助将是惊人的,我已经被困在这个2天,这是我需要完成的最后一点,所以我可以移动到下一个阶段。

子组件是一个函数组件。你不能得到一个函数组件实例的ref,因为没有实例。如果你真的需要使用一个函数组件,并且也需要将一些自定义对象作为ref,那么你可以使用useImperativeHandle钩子加上forwardRef来定义父组件应该在其ref上接收什么。

const Type = React.forwardRef((props, ref) => {
// ...
useImperativeHandle(ref, () => {
// The following object is what will get assigned to the 
//    parent component's this.child.current
return {
afftimer: function () {
tmr = setInterval(tock, 1000);
if (i >= text_array.length) {
clearInterval(tmr);
}
}  
}
});
// ...
})

useImperativeHandle是不常用的东西。可能有一个更标准的方法来解决你的问题。父组件告诉子组件该做什么的通常方式是使用props,而不是ref。

最新更新