要了解 React 试图通过静态方法实现什么,您应该对以下内容有很好的了解:
- 副作用
- 为什么在 componentDidMount 挂钩之前异步代码被认为是一种糟糕的方法
- 异步渲染
- 静态方法如何帮助阻止不纯和异步编码
-
副作用只不过是在范围之外操纵任何数据。因此 getDerivedStateFromProps 中的副作用意味着更改其自身的局部变量以外的任何其他变量。
不会引起副作用的函数称为纯函数,在它们的参数的情况下,它们在被操作之前被克隆,从而保留这些参数指向的对象的状态。
这些函数只是从其范围内返回修改后的值,调用者可以根据返回的数据决定操作过程。
-
在像 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 调用中的过程(他们可以简单地丢弃所有返回的值,直到最后一次调用并使用最后一个)。
-
另一个例子是如果 React 工程师决定让渲染调用异步。也许他们想要合并所有的渲染调用(从父级到所有嵌套的子级)并立即触发它们异步以提高性能。
现在这意味着在渲染方法中或之前编写的异步调用(如在构造函数或 getDerivedStateFromProps 中)可能会由于异步过程完成的不可预测性而干扰渲染过程。一个可能比另一个更早或更晚完成,无法预测地触发它们各自的回调。这种不可预测性可能会以多次渲染、不可预测的状态等形式体现出来。
重要的是,这两个想法都不仅仅是示例,而是被 React 工程师表达为未来可能的优化方法。在这里阅读:https://stackoverflow.com/a/41612993/923372
-
尽管如此,React 工程师知道那里的开发人员仍然可以编写异步代码或不纯函数,为了阻止这种情况,他们将生命周期方法之一设为静态。 构造函数、渲染、getSnapshotBeforeUpdate、componentDidMount和 componentDidUpdate 方法不能是静态的,因为它们需要访问实例属性,如 this.state、this.props、其他自定义事件处理程序等(构造函数初始化它们,render 使用它们来控制 UI 逻辑,其他生命周期方法需要这些将其与早期状态进行比较)
但是考虑到 getDerivedStateFromProps,如果先前的 props 与当前的 props 不同,则仅提供此挂钩以返回状态的更新克隆。根据这个定义,这听起来很纯粹,不需要对实例属性进行任何访问。让我们分析一下原因。
为了使这个钩子起作用,开发者首先需要将先前的 props 存储在实例状态中(比如说,在构造函数调用中)。这是因为 getDerivedStateFromProps 接收实例状态以及新的道具作为参数。然后,开发人员可以继续区分所需的属性并返回状态的更新克隆(无需访问 this.props 或 this.state)。
通过将 getDerivedStateFromProps 设置为静态,React 不仅迫使您编写纯函数,而且还使得编写异步调用变得困难,因为您无法从该方法中访问任何实例。 通常异步调用将提供一个回调,它很可能是一个实例方法。
现在这并不意味着开发人员不能编写它们,而是这只是使它们变得困难并迫使它们远离这些方法。
一个简单的经验法则是在第三方引发的流程期间远离不纯和异步的功能方法。您应该只在此类流程结束时诱导此类方法。