我无法弄清楚为什么this.state未定义。
首次点击开始按钮updateBoard()
并调用startGame()
时。 第一个时间startGame()
被称为运行正常,并在结束时更改状态然后重新呈现电路板。
调用重新渲染componentDidUpdate()
并启动setTimeout()
后,在1000毫秒后调用this.startGame
。运行秒 startGame()
后,我就会......
TypeError: this.state is undefined
代表if()
函数中的第一个startGame()
。
const Board = (props) => {
var size = props.boardSize;
var row = 0;
const toggleCell = (el) => {
var ele = document.getElementById(el);
if(ele.className == 'off'){
ele.className = 'on';
}
else{
ele.className = 'off';
}
}
const cols = (rows) => {
if(props.state.board[0][0] != undefined){
return props.state.board[row].map((data2, index2) => {
var id = '' + row + '-' + index2;
return (<td onClick={toggleCell.bind(this, id)} id={`${rows}-${index2}`} className={`${data2}`}> </td>);
});
}
else{
return props.state.board.map((data2, index2) => {
var rand = Math.floor((Math.random() * 100) + 1);
var choice = '';
if(rand<22){
choice = 'on';
}
else{
choice = 'off';
}
var id = '' + row + '-' + index2;
return (<td onClick={toggleCell.bind(this, id)} id={`${rows}-${index2}`} className={`${choice}`}> </td>);
});
}
}
const genCells = props.state.board.map((data, index) => {
row = index;
return (<tr id={`row${index}`} row={index}> {cols(index)} </tr>);
});
return (
<div>
<div>
<h1 id="title"> The Game of Life </h1>
<h5 id="generations">Gen: {props.state.gens}</h5>
<table id="life">
{genCells}
</table>
</div>
<div id="buttons">
<button onClick={() => {props.createBoard(20)}} className="btn btn-primary sizes"> 20 x 20 </button>
<button onClick={() => {props.createBoard(30)}} className="btn btn-primary sizes"> 30 x 30 </button>
<button onClick={() => {props.createBoard(40)}} className="btn btn-primary sizes"> 40 x 40 </button>
</div>
<div id="options">
<button onClick={() => {props.toggleRun(true); props.startGame();}} className="btn btn-success sizes"> Start </button>
<button onClick={() => {props.toggleRun(false)}} className="btn btn-warning sizes"> Pause </button>
<button onClick={() => {props.clearBoard()}} className="btn btn-danger sizes"> Reset </button>
</div>
</div>
);
}
class App extends React.Component {
constructor(props){
super(props);
this.state={
newGame: true,
gens: 0,
nextGen: [],
size: 0,
board: [],
running: false
}
}
createBoard(dim) {
//If a number is given (60, 80, 120), generate the board
if(dim>0){
//create empty 'board'-Array
var board = [];
//create rows and col in 'board'-Array
for(var i=0;i<dim;i++){
let temp = new Array(dim);
board.push(temp);
}
//set new array to 'board'-State(Array)
this.setState({
board: board,
size: dim
});
}
else{
}
}
updateBoardState(newBoard){
var board = this.state.board;
var length = board.length;
if(newBoard == undefined){
var newBoard = board;
for(var i=0;i<length;i++){
for(var j=0;j<length;j++){
newBoard[i][j] = document.getElementById(''+i+'-'+j).className;
}
}
}
if(newBoard != undefined){
for(var i=0;i<length;i++){
for(var j=0;j<length;j++){
document.getElementById(''+i+'-'+j).className = newBoard[i][j];
}
}
this.setState({board:newBoard,
gens: this.state.gens++});
}
//console.log("board Updated!");
}
toggleRun(boo){
this.updateBoardState();
this.setState({running: boo});
}
clearBoard(){
var ons = document.getElementsByClassName('on');
//console.log(ons);
for(var i=0;i<ons.length;i++){
ons[i].className='off';
i--;
}
}
startGame(){
if(this.state.running == true){
var board = this.state.board;
var size = this.state.size;
var newBoard = board;
for(var i=0;i<size;i++){
for(var j=0;j<size;j++){
//Declarations
let aliveNeighbours = 0;
var target = document.getElementById(''+i+'-'+j);
//Neigbours
var neighblocks = new Array();
neighblocks.push(document.getElementById(''+(i-1)+'-'+(j-1)));
neighblocks.push(document.getElementById(''+(i-0)+'-'+(j-1)));
neighblocks.push(document.getElementById(''+(i+1)+'-'+(j-1)));
neighblocks.push(document.getElementById(''+(i-1)+'-'+(j-0)));
neighblocks.push(document.getElementById(''+(i+1)+'-'+(j-0)));
neighblocks.push(document.getElementById(''+(i-1)+'-'+(j+1)));
neighblocks.push(document.getElementById(''+(i-0)+'-'+(j+1)));
neighblocks.push(document.getElementById(''+(i+1)+'-'+(j+1)));
//Check if Neighbour is alive
//(results in `aliveNeighbours`)
neighblocks.forEach(function(el){
if(el != null){
if(el.className == 'on'){
aliveNeighbours++;
}
}
else{
}
});
//console.log("cell#"+i+"-"+j+" alive neighbours = " + aliveNeighbours);
//RULES
if(aliveNeighbours<2){
//die of isolation
newBoard[i][j] = 'off';
//console.log("neighbour killed!");
}
if(aliveNeighbours == 2){
//live on
if(target.className == 'on'){
newBoard[i][j] = 'on';
//console.log("surviver");
}
}
if(aliveNeighbours > 3){
//die of over population
newBoard[i][j] = 'off';
//console.log("death!");
}
if(aliveNeighbours == 3){
//live on / give birth
newBoard[i][j] = 'on';
//console.log("its a boy!");
}
}
}
for(let a=0;a<size;a++){
for(let b=0;b<size;b++){
var eel = document.getElementById(''+a+'-'+b);
eel.className = newBoard[a][b];
}
}
//this.updateBoardState(newBoard);
}
}
componentWillMount(){
if(this.state.newGame){
this.createBoard(8);
this.setState({newGame: false,
running: true});
}
}
componentDidUpdate(){
var startGamee = this.startGame;
console.log(this.startGame);
setTimeout(this.startGame, 1000);
}
render(){
return (
<div>
<div>
<Board createBoard={this.createBoard.bind(this)}
toggleRun={this.toggleRun.bind(this)}
clearBoard={this.clearBoard.bind(this)}
state={this.state}
updateBoardState={this.updateBoardState.bind(this)}
startGame={this.startGame.bind(this)}
/>
</div>
</div>
)
}
}
ReactDOM.render(<App />, document.getElementById('root'));
答案 0 :(得分:2)
以下是您问题的最小例子:
class Test {
constructor() {
this.name = "test"
}
startGame() {
console.log(this.name)
}
delay() {
setTimeout(this.startGame, 1000)
}
}
let t = new Test
t.startGame() // <- this works
t.delay() // <- probably not what you're hoping for
&#13;
您将在窗口对象中看到delay()
日志undefined
或其他内容。这是因为this
由函数的调用位置决定。在setTimeout
的情况下,this
不再指向您的班级,因为它没有像t.startGame()
那样被调用。 setTimeout
正在调用它,this
指的是setTimeout
认为this
的所有内容。
一种简单的解决方法是在超时时使用箭头功能:
class Test {
constructor() {
this.name = "test"
}
startGame() {
console.log(this.name)
}
delay() {
setTimeout(() => this.startGame(), 1000)
}
}
let t = new Test
t.startGame()
t.delay()
&#13;
另一种方法是使用以下函数传递一个显式绑定到您的类的函数:
delay(){
setTimeout(this.startGame.bind(this), 1000)
}
答案 1 :(得分:2)
在componentDidMount()
方法中,您需要绑定this.startGame()
才能访问状态。见下文:
componentDidUpdate() {
var startGame = this.startGame;
console.log(this.startGame);
setTimeout(this.startGame.bind(this), 1000); // bound to `this`
}
这是因为setTimeout()
是异步的。
答案 2 :(得分:0)
您必须将startGame()
绑定到构造函数中的App
组件,因此该函数具有正确的词法范围。在App
构造函数中:
constructor(props){
super(props);
this.state={
newGame: true,
gens: 0,
nextGen: [],
size: 0,
board: [],
running: false
}
this.startGame = this.startGame.bind(this)
}