使用 componentDidUpdate 相对于 setState 回调有什么优势?
What is the advantage of using componentDidUpdate over the setState callback?
为什么在 React 组件中使用 componentDidUpdate
而不是 setState
回调函数(可选的第二个参数)(如果需要同步 setState 行为)?
由于setState
是异步的,我在考虑使用setState
回调函数(第二个参数)来确保代码在状态更新后执行,类似于then()
为了承诺。特别是如果我需要在后续 setState
调用之间重新渲染。
但是,官方 React 文档说 "The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead."
这就是他们在那里所说的全部内容,所以看起来有点含糊。我想知道是否有更具体的原因建议不要使用它?如果可以的话,我会问问 React 的人。
如果我想按顺序执行多个 setState 调用,就代码组织而言,setState 回调似乎是比 componentDidUpdate 更好的选择 - 回调代码是在 setState 调用中定义的。如果我使用 componentDidUpdate 我必须检查相关状态变量是否改变,并在那里定义后续代码,这不太容易跟踪。此外,在包含 setState 调用的函数中定义的变量将超出范围,除非我也将它们放入状态。
以下示例可能会显示何时使用 componentDidUpdate 可能很棘手:
private functionInComponent = () => {
let someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState(
{ firstVariable: firstValue, }, //firstVariable may or may not have been changed
() => {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += someVariableBeforeSetStateCall;
this.setState({ secondVariable: secondValue });
}
);
}
对
public componentDidUpdate(prevProps. prevState) {
if (prevState.firstVariableWasSet !== this.state.firstVariableWasSet) {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += this.state.someVariableBeforeSetStateCall;
this.setState({
secondVariable: secondValue,
firstVariableWasSet: false,
});
}
}
private functionInComponent = () => {
let someVariableBeforeSetStateCall = this.state.someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState({
firstVariable: firstValue,
someVariableBeforeSetStateCall: someVariableBeforeSetStateCall,
firstVariableWasSet: true });
//firstVariable may or may not have been changed via input,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
}
另外,除了普遍推荐使用componentDidUpdate之外,setState回调在什么情况下更合适?
Why is using componentDidUpdate
more recommended over the setState
callback function?
1。逻辑一致
当使用 setState()
的回调参数时,您可能在不同的地方对 setState()
进行了两次单独的调用,它们都更新了相同的状态,您必须记住使用相同的两地回调。
一个常见的例子是每当状态发生变化时调用 third-party 服务:
private method1(value) {
this.setState({ value }, () => {
SomeAPI.gotNewValue(this.state.value);
});
}
private method2(newval) {
this.setState({ value }); // forgot callback?
}
这可能是一个逻辑错误,因为您可能希望在值更改时随时调用该服务。
这就是推荐 componentDidUpdate()
的原因:
public componentDidUpdate(prevProps, prevState) {
if (this.state.value !== prevState.value) {
SomeAPI.gotNewValue(this.state.value);
}
}
private method1(value) {
this.setState({ value });
}
private method2(newval) {
this.setState({ value });
}
这样,只要状态更新,就可以保证调用服务。
此外,状态可以从外部代码(例如 Redux)更新,您将没有机会为这些外部更新添加回调。
2。批量更新
setState()
的回调参数在组件re-rendered之后执行。但是,由于批处理,多次调用 setState()
不保证会导致多次渲染。
考虑这个组件:
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: ' + this.state.value);
}
onClick = () => {
this.setState(
{ value: 7 },
() => console.log('onClick: ' + this.state.value));
this.setState(
{ value: 42 },
() => console.log('onClick: ' + this.state.value));
}
render() {
return <button onClick={this.onClick}>{this.state.value}</button>;
}
}
我们在 onClick()
处理程序中有两个 setState()
调用,每个调用只是将新的状态值打印到控制台。
您可能希望 onClick()
打印值 7
,然后是 42
。但实际上,它打印了 42
两次!这是因为两个 setState()
调用是一起批处理的,只会导致一个渲染发生。
此外,我们还有一个 componentDidUpdate()
,它也打印新值。由于我们只发生一次渲染,它只执行一次,并打印值 42
.
如果您希望与批量更新保持一致,通常使用 componentDidMount()
.
会容易得多
2.1。什么时候进行批处理?
没关系。
批处理是一种优化,因此您永远不应依赖批处理的发生或不发生。 React 的未来版本可能会在不同的场景中执行或多或少的批处理。
但是,如果您必须知道,在当前版本的 React (16.8.x) 中,批处理发生在异步用户事件处理程序中(例如 onclick
)并且 有时 生命周期方法,如果 React 可以完全控制执行。所有其他上下文从不使用批处理。
查看此答案了解更多信息:
3。什么时候使用 setState
回调比较好?
当外部代码需要等待状态更新时,您应该使用setState
回调而不是componentDidUpdate
,并将其包装在promise中。
例如,假设我们有一个 Child
组件,如下所示:
interface IProps {
onClick: () => Promise<void>;
}
class Child extends React.Component<IProps> {
private async click() {
await this.props.onClick();
console.log('Parent notified of click');
}
render() {
return <button onClick={this.click}>click me</button>;
}
}
我们有一个 Parent
组件,它必须在 child 被点击时更新一些状态:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
private setClicked = (): Promise<void> => {
return new Promise((resolve) => this.setState({ clicked: true }, resolve));
}
render() {
return <Child onClick={this.setClicked} />;
}
}
在 setClicked
中,我们必须创建一个 Promise
到 return 到 child,唯一的方法是将回调传递给 setState
.
无法在 componentDidUpdate
中创建此 Promise
,但即使创建,由于批处理也无法正常工作。
其他
Since setState
is asynchronous, I was thinking about using the setState
callback function (2nd argument) to ensure that code is executed after state has been updated, similar to .then()
for promises.
setState()
的回调完全 与 promises 的工作方式不同,因此最好将您的知识分开。
Especially if I need a re-render in between subsequent setState
calls.
为什么您需要 re-render 在 setState()
调用之间的组件?
我能想到的唯一原因是 parent 组件是否依赖于 child 的 DOM 元素的某些信息,例如它的宽度或高度,以及 parent 根据这些值在 child 上设置一些道具。
在您的示例中,您调用 this.props.functionFromParentComponent()
,它 return 是一个值,然后您使用它来计算某些状态。
首先,应该避免派生状态,因为记忆是一个更好的选择。但即便如此,为什么不让 parent 直接将值作为 prop 向下传递呢?然后你至少可以计算 getDerivedStateFromProps()
.
中的状态值
//firstVariable may or may not have been changed,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
这些评论对我来说意义不大。 setState()
的异步性质并不意味着状态没有得到正确更新。该代码应按预期工作。
为什么在 React 组件中使用 componentDidUpdate
而不是 setState
回调函数(可选的第二个参数)(如果需要同步 setState 行为)?
由于setState
是异步的,我在考虑使用setState
回调函数(第二个参数)来确保代码在状态更新后执行,类似于then()
为了承诺。特别是如果我需要在后续 setState
调用之间重新渲染。
但是,官方 React 文档说 "The second parameter to setState() is an optional callback function that will be executed once setState is completed and the component is re-rendered. Generally we recommend using componentDidUpdate() for such logic instead." 这就是他们在那里所说的全部内容,所以看起来有点含糊。我想知道是否有更具体的原因建议不要使用它?如果可以的话,我会问问 React 的人。
如果我想按顺序执行多个 setState 调用,就代码组织而言,setState 回调似乎是比 componentDidUpdate 更好的选择 - 回调代码是在 setState 调用中定义的。如果我使用 componentDidUpdate 我必须检查相关状态变量是否改变,并在那里定义后续代码,这不太容易跟踪。此外,在包含 setState 调用的函数中定义的变量将超出范围,除非我也将它们放入状态。
以下示例可能会显示何时使用 componentDidUpdate 可能很棘手:
private functionInComponent = () => {
let someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState(
{ firstVariable: firstValue, }, //firstVariable may or may not have been changed
() => {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += someVariableBeforeSetStateCall;
this.setState({ secondVariable: secondValue });
}
);
}
对
public componentDidUpdate(prevProps. prevState) {
if (prevState.firstVariableWasSet !== this.state.firstVariableWasSet) {
let secondVariable = this.props.functionFromParentComponent();
secondVariable += this.state.someVariableBeforeSetStateCall;
this.setState({
secondVariable: secondValue,
firstVariableWasSet: false,
});
}
}
private functionInComponent = () => {
let someVariableBeforeSetStateCall = this.state.someVariableBeforeSetStateCall;
... // operations done on someVariableBeforeSetStateCall, etc.
this.setState({
firstVariable: firstValue,
someVariableBeforeSetStateCall: someVariableBeforeSetStateCall,
firstVariableWasSet: true });
//firstVariable may or may not have been changed via input,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
}
另外,除了普遍推荐使用componentDidUpdate之外,setState回调在什么情况下更合适?
Why is using
componentDidUpdate
more recommended over thesetState
callback function?
1。逻辑一致
当使用 setState()
的回调参数时,您可能在不同的地方对 setState()
进行了两次单独的调用,它们都更新了相同的状态,您必须记住使用相同的两地回调。
一个常见的例子是每当状态发生变化时调用 third-party 服务:
private method1(value) {
this.setState({ value }, () => {
SomeAPI.gotNewValue(this.state.value);
});
}
private method2(newval) {
this.setState({ value }); // forgot callback?
}
这可能是一个逻辑错误,因为您可能希望在值更改时随时调用该服务。
这就是推荐 componentDidUpdate()
的原因:
public componentDidUpdate(prevProps, prevState) {
if (this.state.value !== prevState.value) {
SomeAPI.gotNewValue(this.state.value);
}
}
private method1(value) {
this.setState({ value });
}
private method2(newval) {
this.setState({ value });
}
这样,只要状态更新,就可以保证调用服务。
此外,状态可以从外部代码(例如 Redux)更新,您将没有机会为这些外部更新添加回调。
2。批量更新
setState()
的回调参数在组件re-rendered之后执行。但是,由于批处理,多次调用 setState()
不保证会导致多次渲染。
考虑这个组件:
class Foo extends React.Component {
constructor(props) {
super(props);
this.state = { value: 0 };
}
componentDidUpdate(prevProps, prevState) {
console.log('componentDidUpdate: ' + this.state.value);
}
onClick = () => {
this.setState(
{ value: 7 },
() => console.log('onClick: ' + this.state.value));
this.setState(
{ value: 42 },
() => console.log('onClick: ' + this.state.value));
}
render() {
return <button onClick={this.onClick}>{this.state.value}</button>;
}
}
我们在 onClick()
处理程序中有两个 setState()
调用,每个调用只是将新的状态值打印到控制台。
您可能希望 onClick()
打印值 7
,然后是 42
。但实际上,它打印了 42
两次!这是因为两个 setState()
调用是一起批处理的,只会导致一个渲染发生。
此外,我们还有一个 componentDidUpdate()
,它也打印新值。由于我们只发生一次渲染,它只执行一次,并打印值 42
.
如果您希望与批量更新保持一致,通常使用 componentDidMount()
.
2.1。什么时候进行批处理?
没关系。
批处理是一种优化,因此您永远不应依赖批处理的发生或不发生。 React 的未来版本可能会在不同的场景中执行或多或少的批处理。
但是,如果您必须知道,在当前版本的 React (16.8.x) 中,批处理发生在异步用户事件处理程序中(例如 onclick
)并且 有时 生命周期方法,如果 React 可以完全控制执行。所有其他上下文从不使用批处理。
查看此答案了解更多信息:
3。什么时候使用 setState
回调比较好?
当外部代码需要等待状态更新时,您应该使用setState
回调而不是componentDidUpdate
,并将其包装在promise中。
例如,假设我们有一个 Child
组件,如下所示:
interface IProps {
onClick: () => Promise<void>;
}
class Child extends React.Component<IProps> {
private async click() {
await this.props.onClick();
console.log('Parent notified of click');
}
render() {
return <button onClick={this.click}>click me</button>;
}
}
我们有一个 Parent
组件,它必须在 child 被点击时更新一些状态:
class Parent extends React.Component {
constructor(props) {
super(props);
this.state = { clicked: false };
}
private setClicked = (): Promise<void> => {
return new Promise((resolve) => this.setState({ clicked: true }, resolve));
}
render() {
return <Child onClick={this.setClicked} />;
}
}
在 setClicked
中,我们必须创建一个 Promise
到 return 到 child,唯一的方法是将回调传递给 setState
.
无法在 componentDidUpdate
中创建此 Promise
,但即使创建,由于批处理也无法正常工作。
其他
Since
setState
is asynchronous, I was thinking about using thesetState
callback function (2nd argument) to ensure that code is executed after state has been updated, similar to.then()
for promises.
setState()
的回调完全 与 promises 的工作方式不同,因此最好将您的知识分开。
Especially if I need a re-render in between subsequent
setState
calls.
为什么您需要 re-render 在 setState()
调用之间的组件?
我能想到的唯一原因是 parent 组件是否依赖于 child 的 DOM 元素的某些信息,例如它的宽度或高度,以及 parent 根据这些值在 child 上设置一些道具。
在您的示例中,您调用 this.props.functionFromParentComponent()
,它 return 是一个值,然后您使用它来计算某些状态。
首先,应该避免派生状态,因为记忆是一个更好的选择。但即便如此,为什么不让 parent 直接将值作为 prop 向下传递呢?然后你至少可以计算 getDerivedStateFromProps()
.
//firstVariable may or may not have been changed,
//now someVariableBeforeSetStateCall may or may not get updated at the same time
//as firstVariableWasSet or firstVariable due to async nature of setState
这些评论对我来说意义不大。 setState()
的异步性质并不意味着状态没有得到正确更新。该代码应按预期工作。