React v16.3.0: New lifecycles and context API

幾天前,咱們寫了一篇關於即將到來的對咱們的傳統生命週期方法的變動的文章,包括逐步遷移策略。在React 16.3.0中,咱們添加了一些新的生命週期方法來幫助遷移。咱們還引入了新的API,用於長時間請求的特性:一個官方的上下文API、一個ref轉發API和一個更語意化的ref API。安全

請繼續閱讀,瞭解更多關於這個版本的信息。異步

官方認證的 Context API

多年來,React爲Context提供了一個實驗性的API。雖然它是一個強大的工具,可是因爲API中固有的問題,它的使用是不受歡迎的,所以咱們打算用一個更好的API來替代這實驗性的API。ide

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。this

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將繼續獲得支持。rest

您不須要在組件中替換回調refs。它們稍微靈活一些,所以它們將繼續做爲一個高級特性。code

forwardRef API

高階組件(或HOCs)是在組件之間重用代碼的經常使用方法。基於上面的主題上下文示例,咱們可能會建立一個臨時對象,將當前的「主題」做爲一個屬性注入:component

## 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. 檢測意想不到的反作用
相關文章
相關標籤/搜索