"Good" 如何在 React 中更新状态(从之前的值)
"Good" way how to update state (from its previous value) in React
来自反应文档
从文档 (https://reactjs.org/docs/handling-events.html) 我可以看到通过箭头函数处理开关按钮更改,例如:
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
这里(对我而言)重要的是,新状态值是从其先前的状态值派生出来的。
如果我是对的,行为与(没有箭头功能)相同:
handleClick() {
this.setState(function (prevState){
return {isToggleOn: !prevState.isToggleOn}
});
}
甚至完全没有功能:
handleClick() {
this.setState({isToggleOn: this.state.isToggleOn ?
false : true});
}
1)除代码风格外,这些有什么区别吗?
所有方法似乎都对我有用,我找不到任何区别(除了可读性)。
2)我想,this.setState只是“setter”,如何将“prevState”传递给那个函数?
3)这样写代码(第3种方法)是不是错了?我是老派的,5年多没开发,现在我正在慢慢回到开发中。第一个例子对我来说很难理解(因为箭头函数和匿名函数),第二个例子更清楚一点但最后一个对我来说是最不言自明的。
现实生活中的例子
“真正的学习(生活)”例如我现在正在努力的事情:
在我学习的过程中,我正在尝试编写自己的计算器(仅从基本功能开始)。由于糟糕的“设计”(我发现使用一些数组、字段、列表而不是仅使用“variableA、variableB”会更好,但绝不会少......)。
我的应用程序结构是:
- 计算器
- ResultHeader
- 输入(结果)
- 输入(用于打印等式)
- 计算器按钮 (0)
- 按钮
- ...
- 计算器按钮 (X)
- 按钮
CalcButton 包含其值的道具([0...9]、x、/、+、-、C、...)和事件处理程序(numberClick、operatorClick)。当按下数字按钮时,它会将其状态提升到计算器,计算器将数字添加到 valueA 或 valueB,具体取决于“isFirst”的状态。 operatorClick 包含用于其自身功能的开关,如果按下 x、/、+、-,它将 isFirst 设置为 false,用户可以继续使用第二个数字 (valueB)。现在这个处理程序的代码:
onNumberClick(e) {
function appendA() {
return {valueA: this.state.valueA + e};
}
function appendB() {
return {valueB: this.state.valueB + e};
}
this.setState(this.state.isFirst ? appendA : appendB);
/*
if(this.state.isFirst) {
this.setState({valueA: this.state.valueA + e});
} else {
this.setState({valueB: this.state.valueB + e});
}
*/
}
注释代码与第 3 个示例基本相同,即使是未注释的变体,只是在“可读性”方面有所改进。最后 2 天,如果有任何可能使用一些匿名箭头函数,我会被卡住,所以我不需要复制代码(一次用于 valueA,一次用于 valueB)。由于 javascript 及其缺少对象引用,我能想到的唯一解决方案是通过字符串 ("valueA" || "valueB") 传递参数,但我认为这不是很好的解决方案。这里重要的是(如顶部的示例),新状态值是从其先前的值派生的(这就是为什么我仍在考虑使用箭头匿名函数) .
有人可以告诉我一些如何实现的例子吗?这里是否需要箭头函数(甚至可以通过箭头函数来做到这一点?)还是这个“代码”很好,我发现有问题 none?
React 批处理状态更新。如果在代码的一部分中使用 setState
更新状态,然后代码的其他部分 同步 再次调用 setState
,则先前的值集可能尚未反映在 this.state
中。例如:
class App extends React.Component {
state = {};
componentDidMount() {
this.setState({ foo: 'foo' });
console.log('after setting state, this.state.foo is:', this.state.foo);
// so, further logic that depends on this.state.foo being 'foo' will fail
}
render() {
return 'My App';
}
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
换句话说,你不能可靠地做到
this.setState({ someProp: someVal });
// refer to this.state.someProp here
// because it may well refer to the state before it was updated
// not the new state which contains someVal
如果在您的特定情况下,您没有多个同步状态更新 - 那么就没有问题,因为状态几乎会在单击后立即更新,然后才能处理另一次单击。所以,你的
handleClick() {
this.setState({isToggleOn: this.state.isToggleOn ?
false : true});
}
是安全的——或者更简洁地说:
handleClick() {
this.setState({isToggleOn: !this.state.isToggleOn });
}
只要您不在 handleClick
之前或之后立即执行 else 依赖于或更改 this.state.isToggleOn
.
的操作
你只需要功能版,如
handleClick() {
this.setState(function (prevState){
return {isToggleOn: !prevState.isToggleOn}
});
}
如果状态可能刚刚由上面的代码在重新渲染之前设置(同步)。有关示例,请参见此答案中的第一个片段 - 在这种情况下,如果您想再次调用 this.setState
并使用刚刚设置的 this.state.foo
的值,您需要使用回调形式。
I thought, this.setState is only "setter", how the "prevState" can be passed into that function?
React 内部在调用 setState
时跟踪状态的当前值,并将更新后的值传递给回调,尽管组件的 this.state
可能尚未被调用还没更新。
Is it mistake to write the code like this (3th approach)?
一点也不,只要您了解限制即可。 (如果您的 setState
不依赖于 刚刚通过调用 this.setState
设置 的状态值,那么您的第三种方法就可以了。)
仅举一个人为的例子,如果您的应用程序有一个递增计数器的按钮,以及另一个递增计数器数量的状态值的按钮,一种选择是结合使用回调版本和多个调用 setState
:
updateSum() {
for (let i = 0; i < this.state.count; i++) {
this.incrementByOne();
}
}
incrementByOne() {
this.setState(prev => ({ sum: prev.sum + 1 }));
}
上面的incrementByOne
,因为它被多次调用,只有在使用回调版本时才有效。如果有
this.setState({ sum: this.state.sum + 1 });
它将无法正常运行,因为 this.state.sum
不会从之前对 incrementByOne
的同步调用进行更新。
来自反应文档
从文档 (https://reactjs.org/docs/handling-events.html) 我可以看到通过箭头函数处理开关按钮更改,例如:
handleClick() {
this.setState(prevState => ({
isToggleOn: !prevState.isToggleOn
}));
}
这里(对我而言)重要的是,新状态值是从其先前的状态值派生出来的。 如果我是对的,行为与(没有箭头功能)相同:
handleClick() {
this.setState(function (prevState){
return {isToggleOn: !prevState.isToggleOn}
});
}
甚至完全没有功能:
handleClick() {
this.setState({isToggleOn: this.state.isToggleOn ?
false : true});
}
1)除代码风格外,这些有什么区别吗? 所有方法似乎都对我有用,我找不到任何区别(除了可读性)。
2)我想,this.setState只是“setter”,如何将“prevState”传递给那个函数?
3)这样写代码(第3种方法)是不是错了?我是老派的,5年多没开发,现在我正在慢慢回到开发中。第一个例子对我来说很难理解(因为箭头函数和匿名函数),第二个例子更清楚一点但最后一个对我来说是最不言自明的。
现实生活中的例子
“真正的学习(生活)”例如我现在正在努力的事情: 在我学习的过程中,我正在尝试编写自己的计算器(仅从基本功能开始)。由于糟糕的“设计”(我发现使用一些数组、字段、列表而不是仅使用“variableA、variableB”会更好,但绝不会少......)。 我的应用程序结构是:
- 计算器
- ResultHeader
- 输入(结果)
- 输入(用于打印等式)
- 计算器按钮 (0)
- 按钮
- ...
- 计算器按钮 (X)
- 按钮
- ResultHeader
CalcButton 包含其值的道具([0...9]、x、/、+、-、C、...)和事件处理程序(numberClick、operatorClick)。当按下数字按钮时,它会将其状态提升到计算器,计算器将数字添加到 valueA 或 valueB,具体取决于“isFirst”的状态。 operatorClick 包含用于其自身功能的开关,如果按下 x、/、+、-,它将 isFirst 设置为 false,用户可以继续使用第二个数字 (valueB)。现在这个处理程序的代码:
onNumberClick(e) {
function appendA() {
return {valueA: this.state.valueA + e};
}
function appendB() {
return {valueB: this.state.valueB + e};
}
this.setState(this.state.isFirst ? appendA : appendB);
/*
if(this.state.isFirst) {
this.setState({valueA: this.state.valueA + e});
} else {
this.setState({valueB: this.state.valueB + e});
}
*/
}
注释代码与第 3 个示例基本相同,即使是未注释的变体,只是在“可读性”方面有所改进。最后 2 天,如果有任何可能使用一些匿名箭头函数,我会被卡住,所以我不需要复制代码(一次用于 valueA,一次用于 valueB)。由于 javascript 及其缺少对象引用,我能想到的唯一解决方案是通过字符串 ("valueA" || "valueB") 传递参数,但我认为这不是很好的解决方案。这里重要的是(如顶部的示例),新状态值是从其先前的值派生的(这就是为什么我仍在考虑使用箭头匿名函数) .
有人可以告诉我一些如何实现的例子吗?这里是否需要箭头函数(甚至可以通过箭头函数来做到这一点?)还是这个“代码”很好,我发现有问题 none?
React 批处理状态更新。如果在代码的一部分中使用 setState
更新状态,然后代码的其他部分 同步 再次调用 setState
,则先前的值集可能尚未反映在 this.state
中。例如:
class App extends React.Component {
state = {};
componentDidMount() {
this.setState({ foo: 'foo' });
console.log('after setting state, this.state.foo is:', this.state.foo);
// so, further logic that depends on this.state.foo being 'foo' will fail
}
render() {
return 'My App';
}
}
ReactDOM.render(<App />, document.querySelector('.react'));
<script crossorigin src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>
<div class='react'></div>
换句话说,你不能可靠地做到
this.setState({ someProp: someVal });
// refer to this.state.someProp here
// because it may well refer to the state before it was updated
// not the new state which contains someVal
如果在您的特定情况下,您没有多个同步状态更新 - 那么就没有问题,因为状态几乎会在单击后立即更新,然后才能处理另一次单击。所以,你的
handleClick() {
this.setState({isToggleOn: this.state.isToggleOn ?
false : true});
}
是安全的——或者更简洁地说:
handleClick() {
this.setState({isToggleOn: !this.state.isToggleOn });
}
只要您不在 handleClick
之前或之后立即执行 else 依赖于或更改 this.state.isToggleOn
.
你只需要功能版,如
handleClick() {
this.setState(function (prevState){
return {isToggleOn: !prevState.isToggleOn}
});
}
如果状态可能刚刚由上面的代码在重新渲染之前设置(同步)。有关示例,请参见此答案中的第一个片段 - 在这种情况下,如果您想再次调用 this.setState
并使用刚刚设置的 this.state.foo
的值,您需要使用回调形式。
I thought, this.setState is only "setter", how the "prevState" can be passed into that function?
React 内部在调用 setState
时跟踪状态的当前值,并将更新后的值传递给回调,尽管组件的 this.state
可能尚未被调用还没更新。
Is it mistake to write the code like this (3th approach)?
一点也不,只要您了解限制即可。 (如果您的 setState
不依赖于 刚刚通过调用 this.setState
设置 的状态值,那么您的第三种方法就可以了。)
仅举一个人为的例子,如果您的应用程序有一个递增计数器的按钮,以及另一个递增计数器数量的状态值的按钮,一种选择是结合使用回调版本和多个调用 setState
:
updateSum() {
for (let i = 0; i < this.state.count; i++) {
this.incrementByOne();
}
}
incrementByOne() {
this.setState(prev => ({ sum: prev.sum + 1 }));
}
上面的incrementByOne
,因为它被多次调用,只有在使用回调版本时才有效。如果有
this.setState({ sum: this.state.sum + 1 });
它将无法正常运行,因为 this.state.sum
不会从之前对 incrementByOne
的同步调用进行更新。