React v16.3.0:新的生命周期和上下文API

613 阅读6分钟
原文链接: www.zcfy.cc

作者 Brian Vaughn 2018年3月29日

几天前, 我们 写了一篇关于对以前的生命周期方法进行更改的文章, 包括逐步迁移策略。 在React 16.3.0中, 我们正在添加一些新的生命周期方法来辅助迁移。我们还为长期被要求的功能引入了新的API: 一个官方的context API,一个转发的ref API和一个更符合人类使用的ref API.

继续阅读以了解更多关于该版本的信息。

官方 Context API

多年来,React仅提供了一个实验性的context API。尽管它是一个非常强大的API,但是,因为它固有问题以及我们打算用一个更好的API来替换这个实验的API,所以这个API一般不被推荐使用。

版本16.3引入了一个新的context API,它非常高效并且支持静态类型检查和深度更新。

注意

旧的context API将继续适用于所有React 1.6x版本,所以你将有足够的时间进行迁移。

下面是一个例子,说明如何使用新的context API注入一个『theme』:

const ThemeContext = React.createContext('light');

class ThemeProvider extends React.Component {
  state = {theme: 'light'};

  render() {
    return (
      `<ThemeContext.Provider value={this.state.theme}>`
        {this.props.children}
      `</ThemeContext.Provider>`
    );
  }
}

class ThemedButton extends React.Component {
  render() {
    return (
      `<ThemeContext.Consumer>`
        {theme => `<Button theme={theme} />`}
      `</ThemeContext.Consumer>`
    );
  }
}

了解更多关于新的context API的信息。

createRef API

先前, React 提交供了两种管理refs的方法:传统的字符串形式API和回调函数API。尽管字符串引用API是两者中使用最方便的,但它也有几个缺点 ,所有我们官方的建议是使用回调函数形式代替它。

版本16.3增加了一个管理refs的新的选项,它提供了使用字符串形式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();
  }
}

注意:

除了新的createRef API,Callback refs 将会继续得到支持。

你没有必要替换掉你组件中的callback refs。它们稍微的更灵活一点,所以它们仍然是一个比较先进的功能。

了解更多关于新的 createRef API 的信息.

forwardRef API

高阶组件 (or HOCs) 是在组件间复用的代码的常用方法。基于上面的主题背景示例,我们中以创建一个HOC,它以属性的方式将当前『theme』进行注入:

function withTheme(Component) {
  return function ThemedComponent(props) {
    return (
      `<ThemeContext.Consumer>`
        {theme => `<Component {...props} theme={theme} />`}
      `</ThemeContext.Consumer>`
    );
  };
}

我们可以使用上面的HOC将组件直接连接到主体上下文,而无需直接使用 ThemeContext。例如:

class FancyButton extends React.Component {
  buttonRef = React.createRef();

  focus() {
    this.buttonRef.current.focus();
  }

  render() {
    const {label, theme, ...rest} = this.props;
    return (
      `<button
        {...rest}
        className={`${theme}-button`}
        ref={this.buttonRef}>`
        {label}
      `</button>`
    );
  }
}

const FancyThemedButton = withTheme(FancyButton);

// 我们可以渲染FancyThemedButton,就好像它是一个FancyButton
// 它将会自动接收当前『theme』,
// 同时HOC也会传递我们其它的props。
`<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
/>`;

HOCs通常将会把props传递给它们包裹着的组件。不幸的是, refs不会被传递。这意味着,如果我们使用FancyThemedButton,我们将无法通过ref和FancyButton进行连接,所以我们无法调用focus()

新的forwardRef API解决了这个问题,它提供了一个方法来拦截ref,并将它作为了普通的prop进行传递:

function withTheme(Component) {
  function ThemedComponent({forwardedRef, ...rest}) {
    return (
      `<ThemeContext.Consumer>`
        {theme => (
          // Assign the custom prop "forwardedRef" as a ref
          `<Component
            {...rest}
            ref={forwardedRef}
            theme={theme}
          />`
        )}
      `</ThemeContext.Consumer>`
    );
  }

  // 注意第二个参数"ref"是由React.forwardRef提供的。
  // 我们可以将它作为变通的props传递给ThemedComponent,例如 "forwardedRef"
  // 然后它可以被连接到这个组件。
  return React.forwardRef((props, ref) => (
    `<ThemedComponent {...props} forwardedRef={ref} />`
  ));
}

// 在这里,我们假设FancyButton已经被导入到当前上下文
const FancyThemedButton = withTheme(FancyButton);

// 通过Referenace API创建一个ref,
const fancyButtonRef = React.createRef();

// fancyButtonRef 现在指向 FancyButton
`<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
  ref={fancyButtonRef}
/>`;

组件生命周期变更

React类组件的API已经有好多年了,而且几乎没有变化。但是,当我们添加对更高级功能的支持时(比如错误边界即将到来的异步渲染模式),我们会以不是我们想要的方式来扩展此模型。

例如,使用当前的API,可以很容易的阻止非必要的逻辑进行初始渲染。部分原因是因为完成某项任务的方式太多,而且哪一个最好也不清楚。我们已经观察到错误中断的处理行通常不被考虑在内,并且可能导致内存泄漏(它会影响到即将到来的异步渲染模式)。目前的类组件API也使其他工作复杂化,就像我们在原型化React编译器方面的工作一样。

这些问题在组件生命周期(componentWillMount, componentWillReceiveProps, and componentWillUpdate)的一个子集中更加的恶化。这些也恰好是造成React社区最混乱的生命周期问题。出于这些原因的考虑,我们将会降低这些方法的使用,转而使用更好的方法代替。

我们意识到这些变更将会影响到现有的组件。因此,迁移路径将会是渐进式的,并且会提供补救措施。(在Facebook上,我们维护了超过50,000个React组件。 我们也依赖于一个渐进的发布周期! )

注意:

弃用警告将在未来的16.x版本中启用,但旧版生命周期将继续运行至17.x版。

即使在17.x版中,仍然可以使用它们,但它们会以『UNSAFE_』为前缀被重命名,以表明它们可能会引起问题。我们还准备了一个自动化的脚本来在现有代码中对它们重新命名。

除了摒弃不安全的生命周期外,我们还增加了一些新的生命周期:

了解更多关于生命周期的变更。

StrictMode Component

StrictMode是一个突出显示应用潜在问题的工具。像Fragment一样,StrictMode不会呈现任何可见的UI。它会为子组件作额外的检查并发出警告。

注意:

StrictMode 检查仅仅运行在开发模式下; 它们不影响生产构建

尽管在严格模式下不可能捕获所有的问题(例如:某些类型的突变),但在它在大多数情况下还是很有用的。如果你在严格模式下看到警告,这些东西可能会引起异步渲染的错误。

在16.3版中,StrictMode提供以下帮助:

  • 识别不安全的生命周期组件
  • 对使用字符串ref API进行警告
  • 检测潜在副作用

随着未来React的发版,将会添加更多的功能。

了解更多关于StrictMode 组件的信息.

编辑此页面