gpt4 book ai didi

javascript - 在调用 setState 之前修改 React 组件状态

转载 作者:行者123 更新时间:2023-11-28 16:43:28 24 4
gpt4 key购买 nike

我已阅读并完全理解https://reactjs.org/docs/faq-state.html#what-is-the-difference-between-passing-an-object-or-a-function-in-setstate

问题是,在调用 setState 之前,我仍然遇到状态正在修改的实例。这是一项学习事件,因此请不要推荐像 immutablejs 这样的库。

蛇游戏是我正在制作的。目前使用 step 方法逐帧查看。

render 执行此操作(网格只是循环状态来构建网格):

render() {
return (
<div>
<Grid state={this.state}>
<button onClick={ this.step }>Step Frame</button>
</div>
);
}

step 方法如下所示:

step = () => this.setState(state => {return this.moveSnakeHandler(state)});

哪个调用:

moveSnakeHandler = (prevState : AppState) => {//This returns to setState
console.log(this.state.snake[0], this.state.snake[1]);
//this shows state having x: 25, y: 15} {x: 25, y: 14}

const snake = moveSnake(prevState.snake, prevState.direction);

console.log(...this.state.snake);
//this shows state having {x: 25, y: 15} why is state modified?

return snake;
}

蛇的实际移动(这在组件类之外):

const moveSnake = (prevSnake, direction) => {
//store what the current head is to change direction and placement
const head : {x: number, y: number} = prevSnake[0];

//Tail is the full snake my pop/not below to grow
const tail : Array<{x: number, y: number}> = prevSnake;
switch (direction) {
case "up":
head.y -= 1;
break;
case "down":
head.y += 1;
break;
case "left":
head.x -= 1;
break;
case "right":
head.x += 1;
break;
default:
break;
}
//remove the last element of the new tail so I do not grow
tail.pop();

//toggle pop on isApple when ready

//put the new head and tail together same size as before with everything shifted one!
const snake = [head].concat(tail)

//return the object that will be updated.
return {snake:snake};
}

我的问题是为什么在调用 setState 之前状态会发生变化?

我可以用下面的方法修复 move Snake 中的所有问题,但这似乎没有必要......

const head : {x: number, y: number} = JSON.parse(JSON.stringify(prevSnake[0]));

const tail : Array<{x: number, y: number}> = JSON.parse(JSON.stringify(prevSnake));

最佳答案

您正在直接改变状态。我已将逻辑从 moveSnake 移至 moveSnakeHandler 函数中:

const moveSnakeHandler = (prevState) => {
console.log(this.state.snake[0], this.state.snake[1])
//this shows state having x: 25, y: 15} {x: 25, y: 14}

// head is the object contained in state - do not mutate this
const head = prevState.snake[0]

// tail is the array saved in state - do not mutate this
const tail = prevState.snake

// here we mutate head - this is bad
switch (direction) {
case 'up':
head.y -= 1
break
case 'down':
head.y += 1
break
case 'left':
head.x -= 1
break
case 'right':
head.x += 1
break
default:
break
}

// here we mutate tail - this is bad
tail.pop()

console.log(...this.state.snake)
//this shows state having {x: 25, y: 15}
// we mutated state, so that is why state is modified

return { snake: [head].concat(tail) }
}

现在你看出问题了吗?

您永远不应该改变状态,您应该只调用setState。即使您使用的是 setState 函数:

step = () => this.setState(state => {return this.moveSnakeHandler(state)});

您传递给 moveStateHandler 的值 state 是一个对象,因此您无法修改其属性,因为这意味着您正在修改状态对象。

您可以采取一些措施来防止这种情况:

  • 设置新状态值时切勿使用赋值运算符 (=),例如,不要使用 =+= 或此运算符的任意组合来设置状态。

  • 切勿使用改变当前数组的数组函数。这包括 poppushsplice 等。您应该使用非变异函数,例如 sliceconcat过滤

<小时/>

要解决该问题,您可以执行以下操作:

const moveSnakeHandler = (prevState: AppState) => {
const { snake, direction } = prevState

const newSnakeHead = getNewHead(snake[0], direction)

// We use slice which returns a new array instead of mutating the existing one
// slice(0, -1) returns a new array without the last element
// We create a new array with the new snake head
const newSnake = [newSnakeHead, ...snake.slice(0, -1)]

// Return a new object for the new state
return { ...prevState, snake: newSnake }
}

const getNewHead = (head: { x: number; y: number }, direction: string) => {
// We always return a new object, never mutate the existing head
switch (direction) {
case 'up':
return { ...head, y: head.y - 1 }
case 'down':
return { ...head, y: head.y + 1 }
case 'left':
return { ...head, x: head.x - 1 }
case 'right':
return { ...head, x: head.x + 1 }
default:
return head
}
}

关于javascript - 在调用 setState 之前修改 React 组件状态,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60855838/

24 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com