【问题标题】:Why is getDerivedStateFromProps is a static method?为什么 getDerivedStateFromProps 是静态方法?
【发布时间】:2018-10-19 04:53:56
【问题描述】:

我还没有处理静态 getDerivedStateFromProps,所以我正在尝试了解它。

我了解 React 在 React v16+ 中弃用了 componentWillReceiveProps,引入了一种名为 static getDerivedStateFromProps() 的新生命周期方法。好的,但想知道为什么 React 变成了静态方法而不是普通方法。

为什么

   static getDerivedStateFromProps(nextProps, prevState){

   }

为什么不

   getDerivedStateFromProps(nextProps, prevState){

   }

我无法理解为什么它是一个静态方法。

【问题讨论】:

    标签: javascript reactjs react-native


    【解决方案1】:

    要了解 React 试图通过静态方法实现什么,您应该对以下内容有很好的了解:

    1. 副作用
    2. 为什么在 componentDidMount 挂钩之前异步代码被认为是一种糟糕的方法
    3. 异步渲染
    4. 静态方法如何帮助阻止不纯和异步编码

    1. 副作用只不过是在范围之外操纵任何数据。因此 getDerivedStateFromProps 中的副作用意味着更改其自身的局部变量以外的任何其他变量。

    不会引起副作用的函数称为纯函数,在它们的参数的情况下,它们在被操作之前被克隆,从而保留这些参数指向的对象的状态。

    这些函数只是从其范围内返回修改后的值,调用者可以根据返回的数据决定操作过程。


    1. 在像 React 这样的库中引入自定义异步代码并使用自己的生命周期流程并不是一个好主意。应该在适当的时候小心地插入。让我们通过分析自定义类组件的组件创建生命周期来了解原因(为了保持简短,让我们考虑它也是根元素)。

    一开始 ReactDOM.render 方法调用 react.createElement() 方法调用。

    react.createElement() => 调用 new ClassElement(props) 构造函数 => 返回 ClassElement 实例。

    构造函数调用后,react.createElement() 调用ClassElement.getDerivedStateFromProps(props) 方法调用。

    上述方法返回后,react.createElement()调用instance.render()方法。

    (可以跳过)

    这之后是其他同步调用,例如 diffing with 虚拟 DOM 和更新真实 DOM 等,没有提供钩子 利用这些电话(主要是因为没有强烈的需求)。关键 这里要注意的是 javascript 执行、真实的 DOM 更新和 UI 绘制 - 全部 - 发生在浏览器的单个线程中 强制它们同步。这是您可以编写的原因之一 同步的东西,比如:

    let myDiv = document.getElementbyID("myDiv");
    myDiv.style.width = "300px"; // myDiv already holds a reference to the real DOM element
    console.log(myDiv.style.width); // the width is already set!
    

    因为您知道在每条语句的末尾, 前面的语句在 DOM 和浏览器窗口中完成(UI I 意思)。

    最后,render方法返回后,react.createElement()调用componentDidMount成功标记生命周期结束。 既然结束了,componentDidMount 自然就成为了附加异步和不纯函数的最佳连接点。

    我们必须了解的是,生命周期方法会出于性能和灵活性的原因不断调整,并且完全在 React 工程师的控制之下。它不仅适用于 React,事实上它适用于任何第三方代码的流程。因此,引入不纯函数或异步调用可能会导致问题,因为你会迫使 React 工程师小心优化他们的优化。

    例如如果 React 工程师决定在单个生命周期流中运行两次或更多次 getDerivedStateFromProps,那么不纯函数和异步调用都会被触发两次或更多次,直接影响应用程序的某些部分。但是对于纯函数,这不是问题,因为它们只返回值,并且由 React 工程师决定多个 getDerivedStateFromProps 调用中的过程(他们可以简单地丢弃所有返回的值,直到最后一次调用并使用最后一个)。


    1. 另一个例子是如果 React 工程师决定让渲染调用异步。也许他们想要合并所有的渲染调用(从父级到所有嵌套的子级)并立即触发它们异步以提高性能。

    现在这意味着在渲染方法中或之前编写的异步调用(如在构造函数或 getDerivedStateFromProps 中)可能会由于异步过程完成的不可预测性而干扰渲染过程。一个可能比另一个更早或更晚完成,无法预测地触发它们各自的回调。这种不可预测性可能会以多次渲染、不可预测的状态等形式体现出来。

    重要的是,这两个想法都不仅仅是示例,而是被 React 工程师表达为未来可能的优化方法。在这里阅读:https://stackoverflow.com/a/41612993/923372


    1. 尽管如此,React 工程师知道那里的开发人员仍然可以编写异步代码或不纯函数,为了阻止这种情况,他们将生命周期方法之一设为静态。 构造函数、渲染、getSnapshotBeforeUpdate、componentDidMount和 componentDidUpdate 方法不能是静态的,因为它们需要访问实例属性,如 this.state、this.props、其他自定义事件处理程序等(构造函数初始化它们,render 使用它们来控制 UI 逻辑,其他生命周期方法需要这些将其与早期状态进行比较)

    但是考虑到 getDerivedStateFromProps,如果先前的 props 与当前的 props 不同,则仅提供此挂钩以返回状态的更新克隆。根据这个定义,这听起来很纯粹,不需要对实例属性进行任何访问。让我们分析一下原因。

    为了使这个钩子起作用,开发者首先需要将先前的 props 存储在实例状态中(比如说,在构造函数调用中)。这是因为 getDerivedStateFromProps 接收实例状态以及新的道具作为参数。然后,开发人员可以继续区分所需的属性并返回状态的更新克隆(无需访问 this.props 或 this.state)。

    通过将 getDerivedStateFromProps 设置为静态,React 不仅迫使您编写纯函数,而且还使得编写异步调用变得困难,因为您无法从该方法中访问任何实例。 通常异步调用将提供一个回调,它很可能是一个实例方法。

    现在这并不意味着开发人员不能编写它们,而是这只是使它们变得困难并迫使它们远离这些方法。


    一个简单的经验法则是在第三方引发的流程期间远离不纯和异步的功能方法。您应该只在此类流程结束时诱导此类方法。

    【讨论】:

      【解决方案2】:

      根据这个Proposal的描述:

      本提案旨在降低写作风险 异步兼容的 React 组件。

      它通过消除许多<sup>1</sup> 中的潜在陷阱来做到这一点 当前 API,同时保留 API 的重要功能 启用。我相信这可以通过以下组合来实现:

      1. 选择用途更明确、更有限的生命周期方法名称。

      2. 将某些生命周期设为静态以防止对实例属性的不安全访问。

      还有here

      用静态方法替换容易出错的渲染阶段生命周期钩子 让编写异步兼容的 React 组件变得更容易。

      最终,经过大量讨论,官方也描述了使用静态方法的目标here

      本提案的目标是降低写作风险 异步兼容的 React 组件。我相信可以实现 通过消除当前 API 中的许多潜在缺陷,同时 保留 API 启用的重要功能。这可以做到 通过以下组合:

      1. 选择用途更明确、更有限的生命周期方法名称。

      2. 将某些生命周期设为静态以防止对实例属性的不安全访问。

      不可能检测或预防所有副作用(例如突变 全局/共享对象)。

      【讨论】:

        【解决方案3】:

        您不应该接触该方法中的任何内部数据,因此它被定义为静态。这样,您就没有可以触摸的对象,唯一允许您做的事情就是使用提供的先前状态和下一个道具来做您正在做的任何事情。

        【讨论】:

          【解决方案4】:

          getDerivedStateFromProps 的存在只是为了使组件能够根据 props 的变化更新其内部状态。由于我们只根据 props 更新 state,所以没有理由比较 nextProps 和 this.props。这里我们应该只比较下一个 props 和上一个 state,如果 state 和 props 不同,则更新 state,否则应该没有更新。

          如果我们将 this.props 与 next props 进行比较,我们需要存储旧的 props 值,这会影响性能。保留过去值的副本称为memoization。为避免滥用 “this”memoizationgetDerivedStateFromProps 被设为 static

          我们也可以将上述视为 componentWillReciveProps 贬值的原因。

          【讨论】:

            【解决方案5】:

            getDerivedStateFromProps 是一个新的 API,它是为了在异步渲染作为一项功能发布时可扩展而引入的。根据Dan Abramov in a tweet

            选择此方法是静态的,以帮助确保纯度 很重要,因为它在可中断阶段触发。

            在渲染方法之后移动所有不稳定的东西和副作用的想法。在可中断阶段授予对组件实例变量的访问权限可能会导致人们在使用它时产生各种副作用,从而导致异步渲染不一致

            【讨论】:

              猜你喜欢
              • 2018-10-10
              • 1970-01-01
              • 2014-01-03
              • 2013-08-18
              • 2010-09-28
              • 1970-01-01
              • 1970-01-01
              • 2011-10-11
              • 2011-12-11
              相关资源
              最近更新 更多