React v16.6.0: lazy, memo и contextType

23 октября, 2018 от Sebastian Markbåge

Сегодня мы выпускаем React 16.6 с несколькими новыми удобными возможностями: версию PureComponent/shouldComponentUpdate для функциональных компонентов, способ разделения кода при помощи Suspense и упрощённый доступ к контексту из классовых компонентов.

Полный список изменений описан далее в посте.

React.memo

Классовые компоненты при помощи PureComponent или shouldComponentUpdate могут останавливать рендеринг, если пропсы не изменились. Теперь это доступно и в функциональных компонентах, если обернуть их в React.memo.

const MyComponent = React.memo(function MyComponent(props) {
  /* повторный рендер пройдёт только при изменении пропсов */
});

React.lazy: разделение кода при помощи Suspense

Возможно, вы уже видели доклад Дэна о React Suspense(задержке) на JSConf Iceland. Теперь можно использовать компонент Suspense для разделения кода — нужно просто обернуть динамический импорт в React.lazy().

import React, {lazy, Suspense} from 'react';
const OtherComponent = lazy(() => import('./OtherComponent'));

function MyComponent() {
  return (
    <Suspense fallback={<div>Загрузка...</div>}>
      <OtherComponent />
    </Suspense>
  );
}

Компонент Suspense также даст возможность авторам библиотек в дальнейшем реализовать подгрузку данных с задержкой.

Примечание: эта функциональность пока не доступна для серверного рендеринга. Задержка будет добавлена в будущих релизах.

static contextType

В React 16.3 мы представили официальный API для контекста, который заменит устаревший.

const MyContext = React.createContext();

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

class MyClass extends React.Component {
  static contextType = MyContext;
  componentDidMount() {
    let value = this.context;
    /* выполнить побочный эффект при монтировании, взяв значение из MyContext */
  }
  componentDidUpdate() {
    let value = this.context;
    /* ... */
  }
  componentWillUnmount() {
    let value = this.context;
    /* ... */
  }
  render() {
    let value = this.context;
    /* отрендерить что-нибудь, основываясь на значении из контекста */
  }
}

static getDerivedStateFromError()

React 16 впервые представил предохранители — механизм обработки ошибок, выброшенных при рендере. У нас уже есть метод жизненного цикла, вызываемый после возникновения ошибок, — componentDidCatch — он отлично подходит для отправки логов ошибок на сервер. Плюс к этому, с его помощью можно вызвать setState и показать другой UI.

До вызова этого метода мы рендерим null в точке дерева, где произошла ошибка. Иногда родительские компоненты бывают не готовы к исчезновению рефов на дочерние компоненты и ломаются. Восстановиться после ошибки на сервере также нельзя, потому что Did-методы жизненного цикла при серверном рендере не вызываются.

Мы добавили новый метод для рендера запасного UI до момента, пока не завершится полный рендер. Подробнее о getDerivedStateFromError() вы можете почитать в документации.

Примечание: getDerivedStateFromError() пока что не работает в серверном рендере. Мы выпустили его в таком сыром виде, чтобы вы пораньше с ним освоились и подготовились к релизу, в котором метод заработает и на сервере.

Устаревшие API в StrictMode

В версии 16.3 мы представили компонент StrictMode, который позволяет выводить предупреждения, если в коде используются конструкции, которые в будущем могут вызвать проблемы.

Мы добавили ещё два API в список устаревших API StrictMode. Не беспокойтесь: если вы не используете StrictMode, эти предупреждения у вас не появятся.

  • ReactDOM.findDOMNode() — этот API-метод часто неправильно понимают, и в большинстве случаев он не нужен. К тому же, он может сильно тормозить в React 16. В документации мы описали, чем можно его заменить.
  • Legacy Context — contextTypes и getChildContext слегка замедляют React и добавляют ему лишней громоздкости. Так что мы настоятельно рекомендуем вам перейти на новый API контекста. Мы надеемся, что новый API contextType упростит вам переход.

Если у вас возникнут трудности с обновлением, пожалуйста, дайте нам знать.

Установка

React v16.6.0 уже доступен в реестре npm.

Чтобы установить React 16 при помощи yarn, запустите:

yarn add react@^16.6.0 react-dom@^16.6.0

Чтобы установить React 16 при помощи npm, запустите:

npm install --save react@^16.6.0 react-dom@^16.6.0

Также мы даём возможность скачивать UMD-сборки React через CDN:

<script crossorigin src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

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

Список изменений

React

  • Добавлен React.memo() в качестве альтернативы PureComponent для функциональных компонентов. (@acdlite в #13748)
  • Добавлен React.lazy() для компонентов с разделением кода. (@acdlite в #13885)
  • React.StrictMode теперь предупреждает об устаревшем API контекста. (@bvaughn в #13760)
  • React.StrictMode теперь предупреждает о findDOMNode. (@sebmarkbage в #13841)
  • unstable_AsyncMode переименован в unstable_ConcurrentMode. (@trueadm в #13732)
  • unstable_Placeholder переименован в Suspense, а delayMs — в maxDuration. (@gaearon в #13799 и @sebmarkbage в #13922)

React DOM

  • Добавлен более удобный способ подписки на контекст из классов — contextType. (@bvaughn в #13728)
  • Добавлен метод жизненного цикла для отлова ошибок в новом асинхронном серверном рендерере getDerivedStateFromError. (@bvaughn в #13746)
  • Теперь выводится предупреждение, когда вместо <Context.Consumer> используется <Context>. (@trueadm в #13829)
  • Исправлен серый оверлей в iOS Safari. (@philipp-spiess в #13778)
  • Исправлен баг, вызываемый перезаписью window.event в режиме разработки. (@sergei-startsev в #13697)

React DOM Server

Планировщик (экспериментально)

  • Пакет переименован в scheduler. (@gaearon в #13683)
  • Добавлена поддержка продолжений, обёрнутых колбэков и уровней приоритета. (@acdlite в #13720 и #13842)
  • Улучшен механизм запасного планировщика в среде без DOM. (@acdlite в #13740)
  • requestAnimationFrame теперь принудительно выполняется в начале фрейма. (@acdlite в #13785)
  • Добавлена более тщательная проверка на наличие DOM. (@trueadm в #13731)
  • Исправлены баги счётчика взамодействий. (@bvaughn в #13590)
  • В пакет добавлена envify-трансформация кода. (@mridgway in #13766)