React v16.3.0: новые методы жизненного цикла и API контекста

29 Марта, 2018. Brian Vaughn (Брайан Вон)

Недавно мы написали о предстоящем внесении изменений в наши методы жизненного цикла, включая стратегии постепенной миграции. В React 16.3.0 мы добавляем несколько новых методов жизненного цикла, чтобы помочь с этой миграцией. Также мы внедряем новые API для давно ожидаемых функций: официальный API для контекста, API для передачи ссылки ref и эргономичный API для ref.

Недавно мы написали о предстоящем внесении изменений в наши методы жизненного цикла, включая стратегии постепенной миграции. В React 16.3.0 мы добавляем несколько новых методов жизненного цикла, чтобы помочь с этой миграцией. Также мы внедряем новые API для давно ожидаемых функций: официальный API для контекста, API для передачи ссылки ref и эргономичный API для ref.

Читайте дальше, чтобы узнать больше о релизе.


Официальный API контекста

В версии 16.3 представлен новый API контекста, который более эффективен и поддерживает как статическую проверку типа, так и глубокие обновления.

В течение многих лет React предлагал экспериментальный API для контекста. Хотя это был мощный инструмент, его использование было обескуражено из-за присущих ему проблем с API. Мы всегда собирались заменить экспериментальный API на лучший.

В версии 16.3 представлен новый API контекста, который более эффективен и поддерживает как статическую проверку типа, так и глубокие обновления.


Внимание!

Старый API контекста будет работать для всех релизов React 16.x, поэтому у вас будет время для миграции.

Вот пример, иллюстрирующий, как вы можете вводить «тему» с использованием нового API контекста:


Код
    
  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>
     );
   }
  }
  

Подробнее о новом API контекста можно узнать здесь .


createRef API

Ранее React предоставлял два способа управления ссылками ref: устаревший строковый ref и обратный вызов. Хотя строковый ref был более удобным из них, он имел ряд недостатков, поэтому наша официальная рекомендация заключалась в том, чтобы вместо него использовать обратный вызов.

Версия 16.3 добавляет новую опцию для управления ссылками ref, которая предлагает удобство строки 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 будет поддерживаться и обратный вызов для ref.

Вам не нужно заменять обратные вызовы для ref в ваших компонентах. Они немного более гибкие, поэтому останутся в качестве продвинутой функции.

Подробнее о новом API createRef читайте здесь .


forwardRef API

Компоненты более высокого порядка (или HOC) являются обычным способом повторного использования кода между компонентами. Основываясь на примере выше, мы могли бы создать HOC, который вводит текущую «тему» с помощью свойство:


Код
    
  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 будет передавать наши другие свойства.
  <FancyThemedButton label="Click me!" onClick={handleClick} />;
  

HOC обычно передают props компонентам, которые они обертывают. К сожалению, ссылки не передаются. Это означает, что мы не можем назначить ссылку на FancyButton, если используем FancyThemedButton, поэтому нам не удастся вызвать focus().

Новый forwardRef API решает эту проблему, предоставляя нам возможность перехватить ссылку ref и передать её как обычное свойство:


Код
    
  function withTheme(Component) {
    function ThemedComponent({forwardedRef, ...rest}) {
      return (
        <ThemeContext.Consumer>
          {theme => (
            // Назначаем пользовательское свойство "forwardedRef" как ref
            <Component {...rest} ref={forwardedRef} theme={theme} />
          )}
        </ThemeContext.Consumer>
      );
    }

    // Обратите внимание на второй параметр "ref", предоставленный React.forwardRef.
    // Мы можем передать его компоненту ThemedComponent как обычное свойство, например "forwardedRef".
    // Затем он может быть назначен целевому компоненту.
    return React.forwardRef((props, ref) => (
      <ThemedComponent {...props} forwardedRef={ref} />
    ));
  }
  
  const fancyButtonRef = React.createRef();

  // fancyButtonRef теперь будет указывать на FancyButton
  <FancyThemedButton label="Click me!" onClick={handleClick} ref={fancyButtonRef} />;
  


Изменения методов жизненного цикла компонента

API React класса компонента в течение многих лет претерпевал небольшие изменения. Однако, поскольку мы добавляем поддержку более продвинутых функций (таких как границы ошибок и предстоящий асинхронный режим отрисовки), мы расширяем эту модель таким образом, что ничего подобного изначально это не предполагалось.

Например, с текущим API очень легко блокировать начальную отрисовку с несущественной логикой. Частично это связано с тем, что существует слишком много способов выполнить данную задачу, и может быть неясно, какой из них лучше. Мы заметили, что прерывание процесса обработки ошибок часто не учитывается и может привести к утечкам памяти (что также повлияет на предстоящий режим асинхронной отрисовки). Текущий API класса компонента также усложняет другие вещи, такую как наша работа по прототипированию компилятора React .

Многие из этих проблем усугубляются подмножеством следующих методов жизненного цикла компонента: componentWillMount, componentWillReceiveProps и componentWillUpdate. Они вызывают наибольшую путаницу в сообществе React. По этим причинам мы хотим сделать эти методы устаревшими в пользу лучших альтернатив.

Мы признаем, что это изменение повлияет на многие существующие компоненты. Из-за этого путь миграции будет как можно более постепенным и предоставит необходимые способы обхода проблем. (В Facebook мы поддерживаем более 50 000 компонентов React. Мы также зависим от постепенного цикла релиза!)


Внимание!

Предупреждения об устаревших компонентах будут доступны в будущих 16.x релизах, но устаревшие методы жизненного цикла будут продолжать работать до 17 версии.

Даже в 17 версии их использование все равно будет возможно. Однако эти методы будут наделены префиксом «UNSAFE_» для указания, что они могут вызвать проблемы. Мы также подготовили автоматический скрипт , чтобы переименовать их в существующем коде.

Помимо устаревших небезопасных методов жизненного цикла, мы одновременно добавляем пару новых методов:

  • getDerivedStateFromProps будет добавлен как более безопасная альтернатива устаревшему componentWillReceiveProps.
  • getSnapshotBeforeUpdate добавляется для обеспечения безопасного считывания свойств, например с DOM, перед выполнением обновлений.

Подробнее об этих изменениях читайте здесь.


Компонент StrictMode

StrictMode - это инструмент для выделения потенциальных проблем в приложении. Как и Fragment, StrictMode не отрисовывает видимый UI. Он активирует дополнительные проверки и предупреждения для своих потомков.


Внимание!

Проверки StrictMode выполняются только в режиме разработки; они не влияют на продакшен билд.

Хотя для strict mode невозможно устранить все проблемы (например, определенные типы мутаций), это может многим помочь. Если вы видите предупреждения в strict mode, эти вещи, скорее всего, приведут к багам при асинхронной отрисовке.

В версии 16.3, StrictMode помогает с:

  • Определением компонентов с небезопасными методами жизненного цикла.
  • Предупреждения об использовании строкового ref API.
  • Обнаружение неожиданных сторонних эффектов.

Дополнительный функционал будет добавлен в будущих релизах React.

Подробнее о компоненте StrictMode читайте здесь.