21

几天前,我们写了一篇关于即将到来的对我们的传统生命周期方法的变更的文章,包括逐步迁移策略。在React 16.3.0中,我们添加了一些新的生命周期方法来帮助迁移。我们还引入了新的API,用于长时间请求的特性:一个官方的上下文API、一个ref转发API和一个更语意化的ref API。

请继续阅读,了解更多关于这个版本的信息。

官方认证的 Context API

多年来,React为Context提供了一个实验性的API。虽然它是一个强大的工具,但是由于API中固有的问题,它的使用是不受欢迎的,因此我们打算用一个更好的API来替代这实验性的API。

React 16.3引入了一个新的Context API,它更高效,同时支持静态类型检查和深度更新。

注意
旧的ContextAPI 将继续保留到React 16.x,所以您将有时间迁移。

下面是一个示例,说明如何使用新的上下文API注入“主题”:

## by 司徒正美
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>
    );
  }
}

createRef API

以前,React提供了两种管理refs的方法:字符串ref API和回调ref API。尽管字符串ref API比较方便,但是它有几个缺点,所以我们的官方推荐是使用回调ref。

React 16.3为管理refs提供了一个新的方案,它为字符串ref提供了方便,并且没有任何缺点:

## by 司徒正美

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外,回调refs将继续得到支持。

您不需要在组件中替换回调refs。它们稍微灵活一些,因此它们将继续作为一个高级特性。

forwardRef API

高阶组件(或HOCs)是在组件之间重用代码的常用方法。基于上面的主题上下文示例,我们可能会创建一个临时对象,将当前的“主题”作为一个属性注入:

## by 司徒正美

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

我们可以使用上述特殊的方式将组件连接到主题上下文,而不必直接使用主题上下文。例如:

## by 司徒正美

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);

// We can render FancyThemedButton as if it were a FancyButton
// It will automatically receive the current "theme",
// And the HOC will pass through our other props.
<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
/>;

HOCs通常会将props传递给它们包装的组件。不幸的是,refs没有冲透进去。这意味着如果我们使用FancyThemedButton,我们就不能将ref添加到FancyButton中,因此我们无法调用focus()。

新的代理API通过提供一种方法来拦截一个ref,并将其转发为一个普通的props,从而解决了这个问题:

## by 司徒正美

function withTheme(Component) {
  // Note the second param "ref" provided by React.forwardRef.
  // We can attach this to Component directly.
  function ThemedComponent(props, ref) {
    return (
      <ThemeContext.Consumer>
        {theme => (
          <Component {...props} ref={ref} theme={theme} />
        )}
      </ThemeContext.Consumer>
    );
  }

  // These next lines are not necessary,
  // But they do give the component a better display name in DevTools,
  // e.g. "ForwardRef(withTheme(MyComponent))"
  const name = Component.displayName || Component.name;
  ThemedComponent.displayName = `withTheme(${name})`;

  // Tell React to pass the "ref" to ThemedComponent.
  return React.forwardRef(ThemedComponent);
}

const fancyButtonRef = React.createRef();

// fancyButtonRef will now point to FancyButton
<FancyThemedButton
  label="Click me!"
  onClick={handleClick}
  ref={fancyButtonRef}
/>;

组件生命周期钩子的变化

React的类组件API已经存在多年,几乎没有变化。但是,当我们为更高级的特性(例如错误边界和即将到来的异步渲染模式)添加支持时,我们以它本来没有打算的方式来扩展这个模型。

例如,在当前的API中,用一些非寻常的手段来阻止初始渲染是很容易的。在某种程度上,这是因为有太多的钩子来完成这项既定的任务,而且还不清楚哪一个是最好的。我们已经注意到错误处理的中断行为通常不会被考虑,并且可能导致内存泄漏(这也会影响即将到来的异步渲染模式)。当前的类组件API也使其他的工作变得复杂,比如我们的代码优化器(Prepack)的工作。

componentWillMount, componentWillReceiveProps, componentWillUpdate这些钩子很容易引发问题,并且也严重扰乱React的生命周期。基于这些原因,我们将废弃这些方法,以支持更好的替代方案。

我们认识到这一变化将影响许多现有的组件。因此,迁移路径将尽可能平缓,并提供迁移方案。(在Facebook,我们拥有5万多个React组件。我们也依赖于一个渐进的发布周期!

注意

弃用警告将在React16以后的版本中启用, 一直保留到17发布时。

即使在React17中,仍然可以使用它们,但是它们将添加“UNSAFE_”前缀,以表明它们可能导致问题。我们还准备了一个自动化的脚本,以便现有代码中重命名它们。

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

getDerivedStateFromProps 用来componentWillReceiveProps。

getSnapshotBeforeUpdate,用在更新前从DOM中安全地读取属性。

StrictMode 组件

<StrictMode />是一种专门用于暴露潜在问题的工具。与<Fragment />一样,<StrictMode/>将 不会渲染到视图中。它能为其子组件激活额外的检查和警告。

注意

<StrictMode />检查只在开发模式下运行;它们不会影响生产构建。

虽然严格的模式不可能捕获所有的问题(例如某些类型的窜改),但它可以帮助很多人。如果您在严格的模式下看到警告,这些事情很可能会导致异步渲染的错误。

在16.3版本中,StrictMode帮助:

  1. 识别具有不安全生命周期钩子的组件。
  2. 关于遗留字符串ref API用法的警告。
  3. 检测意想不到的副作用

司徒正美
5.6k 声望3.5k 粉丝

穿梭于二次元与二进制间的魔法师( ̄(工) ̄) 凸ส้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้้