严格模式(Strict Mode)

StrictMode 是一个用以标记出应用中潜在问题的工具。就像 FragmentStrictMode 不会渲染任何真实的UI。它为其后代元素触发额外的检查和警告。

注意: 严格模式检查只在开发模式下运行,不会与生产模式冲突

你可以在应用的任何地方启用严格模式。例如:

import React from 'react';

function ExampleApplication() {
  return (
    <div>
      <Header />
      <React.StrictMode>
        <div>
          <ComponentOne />
          <ComponentTwo />
        </div>
      </React.StrictMode>
      <Footer />
    </div>
  );
}

在上面的例子中,不会对组件 HeaderFooter 进行 strict mode 检查。然而 ComponentOneComponentTwo以及它们所有的后代将被检查。

StrictMode目前有助于:

将来的React版本将添加其他功能。

识别具有不安全生命周期的组件

如同在 这篇博客 中阐明的,在异步 React 应用中使用某些老式的生命周期方法不安全。但是, 如果应用程序使用第三方库, 则很难确保不使用这些生命周期方法。幸运的是, 严格的模式可以帮助解决这个问题!

当启用严格模式, React将编译一个所有使用不安全生命周期组件的列表,并打印一条关于这些组件的警告信息,就像:

strict mode unsafe lifecycles warning

有关旧式字符串ref用法的警告

以前,React提供了2种方法管理ref:旧式的字符串ref API和回调API。虽然字符串ref API更加方便,但它有些许缺点,因此我们的正式建议是改用回调方式

React 16.3新增了第三种方式, 它提供了字符串 ref 的方便性, 而没有任何缺点:

class MyComponent extends React.Component {
  constructor(props) {
    super(props);

    this.inputRef = React.createRef();
  }

  render() {
    return <input type="text" ref={this.inputRef} />;
  }

  componentDidMount() {
    this.inputRef.current.focus();
  }
}

由于新增的对象式refs很大程度上作为字符串ref的替换,因此strict mode现在对字符串ref的用法发出警告。

注意: 除了新的 createRef API,回调ref将被继续支持。您不需要在组件中替换回调ref。它们稍微灵活一些, 因此它们将保持为高级功能。

您不需要替换组件中的回调 refs 。它们稍微灵活一些,所以他们仍然是一个先进的功能。

学习更多有关createRef API内容

关于已弃用的 findDOMNode 用法的警告

React 用 findDOMNode 来支持搜索给定类实例的 DOM 节点。通常不需要这样做,因为可以 直接将 ref 附加到 DOM 节点上

findDOMNode 也可用于类组件上,但这通过允许父级要求渲染某些子级,来打破抽象级别。它会产生重构风险,您无法更改组件的实现细节,因为父级可能会访问其 DOM 节点。 findDOMNode 只返回第一个子节点,但是使用 Fragments ,组件可以渲染多个DOM节点。 findDOMNode 是一次性读取 API。当你要求时,它只给你一个答案。如果子组件渲染不同的节点,则无法处理此更改。因此,findDOMNode 仅在组件始终返回永不更改的单个 DOM 节点时才起作用。

您可以通过将 ref 传递给您的自定义组件并使用 ref 转发将其传递给 DOM 来改为显式。

您还可以在组件中添加包裹的 DOM 节点,并将 ref 直接附加到它。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.wrapper = React.createRef();
  }
  render() {
    return <div ref={this.wrapper}>{this.props.children}</div>;
  }
}

注意:

在 CSS 中, 如果不希望节点成为布局的一部分,则可以使用 display: contents属性。

检测意外的副作用

理论上,React在两个阶段起作用:

  • 渲染 阶段决定了需要对 DOM 进行哪些更改。在此阶段, React调用render(方法), 然后将结果与上一次渲染进行比较。
  • 提交 阶段是React执行任何更改的阶段。(在React DOM中, 指React插入、更新和删除 dom 节点)。在此阶段React也调用生命周期, 如 componentDidMountcomponentDidUpdate

提交阶段通常很快,但是渲染可能很慢。因此, 即将出现的异步模式 (默认情况下尚未启用) 将呈现工作分解为片断, 暂停和恢复工作以避免阻止浏览器。这意味着在提交之前, 反应可能不止一次地调用渲染阶段生命周期, 或者它可以在不提交的情况下调用它们 (因为错误或更高的优先级中断)。

渲染阶段的生命周期包括以下class component方法:

  • constructor
  • componentWillMount
  • componentWillReceiveProps
  • componentWillUpdate
  • getDerivedStateFromProps
  • shouldComponentUpdate
  • render
  • setState 更新函数 (第一个形参)

因为以上方法可能不止一次被调用,所以它们中不包含副作用尤为重要。忽略此规则可能会导致各种问题, 包括内存泄漏和无效的应用程序状态。不幸的是, 很难发现这些问题, 因为它们通常都是不确定的

严格模式不能自动检测到你的副作用, 但它可以帮助你发现它们, 使其更具确定性。这是通过有意地双调用以下方法来完成的:

  • Class component constructor
  • render
  • setState 更新函数 (第一个形参)
  • static getDerivedStateFromProps

注意: 只在开发模式生效。生产模式下生命周期不会被双调用

举个例子,考虑以下代码:

class TopLevelRoute extends React.Component {
  constructor(props) {
    super(props);

    SharedApplicationState.recordEvent('ExampleComponent');
  }
}

乍一看, 这段代码似乎没有问题。但是如果 SharedApplicationState.recordEvent 不是幂等, 那么多次实例化此组件可能会导致无效的应用程序状态。这种微妙的 bug 可能不会在开发过程中显现出来, 或者它可能会不一致, 因此被忽略。

通过有意的双调用方法 (如组件构造函数), 严格模式使得这样的行为更容易被发现。

检测遗留 context API

遗留 context API 容易出错,将在未来的主要版本中删除。 它仍适用于所有 16.x 版本,但会在严格模式下显示此警告消息:

warn legacy context in strict mode

阅读 新的 context API 文档 以帮助你迁移到新版本。