当前位置: 首页 > 面试题库 >

通过setState回调使用componentDidUpdate有什么好处?

蒯硕
2023-03-14
问题内容

为什么在React组件中(如果需要同步setState行为)componentDidUpdate建议在setState回调函数(可选的第二个参数)上使用更多功能?

由于setState是异步的,因此我在考虑使用setState回调函数(第二个参数)来确保状态更新后执行代码,这与then()promise
类似。特别是如果我需要在后续setState调用之间重新渲染。

但是,官方的React Docs说:“
setState()的第二个参数是一个可选的回调函数,将在setState完成并重新渲染组件后执行。通常,我们建议将componentDidUpdate()用于此类逻辑。”
那就是他们在那儿所说的一切,所以似乎有点含糊。我想知道是否还有更具体的原因建议不要使用它?如果可以的话,我想问一下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回调更适合使用?


问题答案:

为什么componentDidUpdatesetState回调函数上推荐使用更多?

在使用callback参数时setState(),您可能setState()在不同的地方有两个单独的调用,这两个调用都会更新相同的状态,因此您必须记住在两个地方都使用相同的回调。

一个常见的示例是每当状态发生变化时就调用第三方服务:

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()重新呈现组件后,执行callback参数。但是,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>;
  }
}

我们setState()onClick()处理程序中有两个调用,每个调用仅将新的状态值打印到控制台。

您可能希望onClick()先打印该值7,然后再打印42。但是实际上,它会打印42两次!这是因为两个setState()调用被批处理在一起,并且仅导致一个渲染发生。

另外,我们还有一个componentDidUpdate()也可以打印新值。由于我们只有一个渲染发生,因此它只执行一次,并打印value 42

如果您希望与批处理更新保持一致,通常使用起来会容易得多componentDidMount()

2.1。何时分批进行?

没关系

批处理是一种 优化 ,因此,您永远不应依赖已发生或未发生的批处理。未来版本的React可能会在不同情况下执行或多或少的批处理。

但是,如果您必须知道,在当前版本的React(16.8.x)中,如果React可以完全控制执行,则批处理将在异步用户事件处理程序(例如onclick)中进行,
有时在 生命周期方法中进行。所有其他上下文从不使用批处理。

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组件,当孩子被点击时,该组件必须更新某些状态:

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返回子级的子项,唯一的方法是将回调传递给setState

这是不可能创建此PromisecomponentDidUpdate,但即使是这样,它不会正确由于批处理工作。

杂项

由于setState是异步的,因此我在考虑使用setState回调函数(第二个参数)来确保状态更新后执行代码,这与.then()promise
类似。

回调setState()相当 的工作方式相同的承诺做的,所以它可能是最好分开你的知识。

特别是如果我需要在后续setState调用之间重新渲染。

为什么您需要在两次setState()调用之间重新渲染组件?

我能想象的唯一原因是父组件是否依赖于子DOM元素的某些信息(例如其宽度或高度),并且父组件基于这些值在子组件上设置了一些道具。

在您的示例中,您调用this.props.functionFromParentComponent(),它返回一个值,然后将其用于计算某些状态。

首先,应该避免派生状态,因为记忆是一个更好的选择。但是即使如此,为什么不让父母直接将值作为道具传递呢?然后,您至少可以计算中的状态值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()并不表示有关状态无法正确更新的任何信息。该代码应按预期工作。



 类似资料:
  • 问题内容: 我正在编写一个脚本,该脚本根据下拉列表的高度和输入在屏幕上的位置将下拉列表移动到输入下方或上方。我也想将修改器设置为根据其方向下拉。但是在内部使用会产生无限循环(这很明显) 我已经找到了使用和直接将classname设置为下拉列表的解决方案,但是我觉得应该使用React工具有更好的解决方案。有谁能够帮我? 这是工作代码的一部分(略微忽略了定位逻辑,以简化代码) 这是带有setstate

  • 当react组件状态发生更改时,将调用render方法。因此,对于任何状态更改,都可以在呈现方法体中执行操作。那么setState回调是否有特定的用例?

  • 本文向大家介绍你有使用过render函数吗?有什么好处?相关面试题,主要包含被问及你有使用过render函数吗?有什么好处?时的应答技巧和注意事项,需要的朋友参考一下 template也会翻译成render,只有一点,template中元素的tag_name是静态的,不可变化,使用createEelment可以生成不同tag_name, 比如h1 ... h6, 可以通过一个number变量控制

  • 问题内容: 我的应用程序中有一些内存泄漏。它们都起源于一个特定的视图集群,我花了大量时间进行调整,并尝试减少尽可能多的上下文传递。这使我相信群集中使用的位图就是问题所在。因此,我考虑将WeakReferences用于所有对视图使用的位图的引用。我从未使用过WeakReference,并且不确定这是否是一个好的应用程序。任何机构都可以提供有用的指示或技巧吗? 问题答案: 因此,我考虑将WeakRef

  • 本文向大家介绍使用黄瓜有什么好处?,包括了使用黄瓜有什么好处?的使用技巧和注意事项,需要的朋友参考一下 下面列出了使用黄瓜的一些优点- Cucumber是一种开源工具,不需要许可。 黄瓜可以通过Eclipse等IDE轻松配置。 黄瓜弥合了开发人员,测试人员,业务分析师,客户和产品所有者之间的理解和沟通差距。 黄瓜使没有技术知识的业务利益相关者参与其中。 黄瓜提供纯文本表示形式,使团队中的非技术人员

  • 问题内容: 该功能运行什么?它只会运行吗? 问题答案: setState()将按以下顺序运行函数: 如果您的组件正在接收道具,它将使用上述功能运行该功能。