React

Практически каждый рад, когда выходит новая версия любимого мобильного приложения или OC. Разработчик тоже должен быть рад выходу свежего обновления для своего любимого фреймворка, так как оно привносит в него новые функции и разные полезные штучки.

Ниже приведен список некоторых полезных функций, которые появились в новой версии React, и которые следует учитывать при переносе существующего приложения с React 15 на React 16.

Пришло время прощаться, React 15 👋

Обработка ошибок

 

React 16 представил пользователям новую функцию под названием error boundary.

Error boundaries — это специальные компоненты React, которые перехватывают ошибки JavaScript в любом месте своего поддерева. Затем они регистрируют эти ошибки и выводят резервный UI, вместо поврежденных компонентов поддерева. Error boundaries захватывают ошибки во время рендеринга, жизненного цикла компонента и в конструкторах всего дерева под ними.

Компонент класса становится error boundary, если он определяет новый жизненный цикл компонента, называемый componentDidCatch(error, info):

class ErrorBoundary extends React.Component {
  constructor(props) {
    super(props);
    this.state = { hasError: false };
  }

  componentDidCatch(error, info) {
    // Display fallback UI
    this.setState({ hasError: true });
    // You can also log the error to an error reporting service
    logErrorToMyService(error, info);
  }

  render() {
    if (this.state.hasError) {
      // You can render any custom fallback UI
      return <h1>Something went wrong.</h1>;
    }
    return this.props.children;
  }
}

Затем вы можете использовать его как обычный компонент.

<ErrorBoundary>  
   <MyWidget />
</ErrorBoundary>

Метод componentDidCatch() работает также как JavaScriptcatch {} block, но для компонентов. Только компоненты класса могут быть error boundaries. На практике, в большинстве случаев требуется единожды объявить error boundary компонент, затем вы сможете использовать его во всем приложении.

Обратите внимание, что error boundaries перехватывают ошибки только в компонентах, расположенных в поддеревеError boundary не может перехватить ошибку внутри себя. Если error boundary не удается отобразить сообщение об ошибке, ошибка распространится на ближайшую error boundary над ней. Это очень похоже на то, какcatch {} block работает в JavaScript.

Можете посмотреть демо-версию:

 

Для получения дополнительной информации о обработке ошибок, загляните сюда.

Новые типы для рендера: фрагменты и строки

Теперь вы можете вернуть массив элементов из render метода компонента. Как и с другими массивами, вам нужно добавить ключ к каждому элементу, чтобы избежать предупреждения:

render() {
  // No need to wrap list items in an extra element!
  return [
    // Don't forget the keys 🙂
    <li key="A">First item</li>,
    <li key="B">Second item</li>,
    <li key="C">Third item</li>,
  ];
}

Начиная с версии 16.2.0, React поддерживает специальный синтаксис JSX, который не требует явного указания ключей.

В React 16 добавили поддержку для возврата строк:

render() {
  return 'Look ma, no spans!';
}

Порталы

Порталы предоставляют первоклассный способ рендера дочерних компонентов в DOM-узел, который существует вне иерархии родительского компонента.

ReactDOM.createPortal(child, container)

Первым аргументом (child) является любой рендерируемый дочерний элемент React, такой как element, string или fragment. Второй аргумент (container) является элементом DOM.

Как его использовать?

При возврате элемента из render метода компонента, он подключается в DOM, как дочерний элемент ближайшего родительского узла:

render() {
  // React mounts a new div and renders the children into it
  return (
    <div>
      {this.props.children}
    </div>
  );
}

Иногда полезно вставлять дочерний элемент в другое положение в DOM:

render() {
  // React does *not* create a new div. It renders the children into `domNode`.
  // `domNode` is any valid DOM node, regardless of its location in the DOM.
  return ReactDOM.createPortal(
    this.props.children,
    domNode
  );
}

Типичный случай использования порталов — это когда родительский компонент имеет overflow: hidden или z-index свойство, но вам нужно, чтобы дочерний элемент визуально “вырвался наружу” из своего контейнера. Например, это могут быть диалоги, всплывающие окна и подсказки.

 

Произвольные DOM-атрибуты

 

React 15 игнорировал любые неизвестные DOM-атрибуты.

// Your code:
<div mycustomattribute="something" />

React 15 отображал пустой div в DOM:

// React 15 output:
<div />

В React 16 результат будет следующим (пользовательские атрибуты не будут игнорироваться):

// React 16 output:
<div mycustomattribute="something" />

Избегайте Re-render с установкой null in state

 

С помощью React 16 вы можете предотвратить обновление состояния и re-render из setState(). Для этого вам нужно, чтобы ваша функция вернула null.

const MAX_PIZZAS = 20;
function addAnotherPizza(state, props) {
  // Stop updates and re-renders if I've had enough pizzas.
  if (state.pizza === MAX_PIZZAS) {
    return null;
  }
  // If not, keep the pizzas coming! 😀
  return {
    pizza: state.pizza + 1,
  }
}
this.setState(addAnotherPizza);

Подробнее об этом здесь.

Создание рефов

Создание рефов с помощью React 16 теперь намного проще. Вот почему вам нужно использовать рефы:

  • Фокус элемента, выделение текста или воспроизведение мультимедиа
  • Анимации
  • Интеграции с DOM-библиотеками

Рефы создаются при помощи React.createRef() и крепятся к элементам React через атрибут ref. Рефы используются для получения ссылки на узел DOM или компонента в React.

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

Доступ к рефам

Когда реф передается элементу в render, ссылка на узел становится доступной в current атрибуте рефа.

const node = this.myRef.current;

Значение рефа различается, в зависимости от типа узла:

  • Когда атрибут ref используется для HTML-элемента, ref, созданный в конструкторе с React.createRef(), получает базовый DOM элемент в качестве current свойства.
  • Когда атрибут ref используется для компонента пользовательского класса, объект ref получает вмонтированный экземпляр компонента в качестве current.
  • Атрибут ref нельзя использовать для функциональных объектов, так как у них нет экземпляров.

Context API

Контекст предоставляет способ передачи данных через дерево компонентов без необходимости передавать реквизиты вручную на каждом уровне.

React.createContext

const {Provider, Consumer} = React.createContext(defaultValue);

Создает пару { Provider, Consumer }. Когда React будет выводить Consumer, он считает текущее значение контекста из ближайшего соответствующего Provider выше в дереве иерархии.

Аргумент defaultValue используется при отрисовке Consumer, не имеющего соответствующего Provider выше него в дереве. Это может быть полезно при тестировании компонентов изолированно, без обертывания. Примечание: передача undefined в качестве значения Provider не приведет к тому, что Consumer будет использовать defaultValue.

Provider

<Provider value={/* some value */}>

Компонент React, который позволяет Consumer подписываться на изменения контекста.

Принимает свойство value, которое должно быть передано Consumer, которые являются потомками данного Provider. Один Providerможет быть соединен со многими ConsumerProvider могут быть вложенными для предопределения значений глубже в дереве.

Consumer

<Consumer>
  {value => /* render something based on the context value */}
</Consumer>

Компонент React, подписывающийся на изменения контекста.

Требует функцию в качестве дочернего элемента. Функция получает текущее значение контекста и возвращает узел React. Аргумент value, переданный функции, будет равен свойству value ближайшего Provider для этого контекста выше в дереве. Если для данного контекста выше нет Provider, аргумент value будет равен значению defaultValue, которое было передано в createContext().

static getDerivedStateFromProps()

getDerivedStateFromProps вызывается непосредственно перед вызовом метода render. Как при первоначальном подключении, так и при последующих обновлениях. Он должен возвращать объект, которым будет обновлено состояние, или null, если состояние трогать не надо.

Этот метод существует для тех редких случаев, когда состояние зависит от изменений свойств (props) с течением времени. Например, удобно реализовать компонент <Transition>, который сравнивает свои предыдущие и следующие дочерние элементы, чтобы решить, какие из них анимировать снаружи и внутри.

Вывод состояния приводит к трудночитаемому коду и затрудняет понимание компонентов.

Убедитесь, что вы знакомы с более простыми альтернативами:

  • Если вам необходимо выполнить побочный эффект (например, выборку данных или анимацию) в ответ на изменение свойства (props), используйте жизненный цикл componentDidUpdate.
  • Если вы хотите пересчитать некоторые данные только при изменении свойств (props), используйте мемоизацию.
  • Чтобы сбросить значение при изменении свойства (props), подумайте о том, чтобы сделать компонент полностью контролируемым или полностью неконтролируемым с помощью key.

Этот метод не имеет доступа к экземпляру компонента. Если хотите, вы можете повторно использовать код между getDerivedStateFromProps() и другими методами класса, извлекая чистые функции свойства компонента и состояние вне определения класса.

Обратите внимание, что этот метод запускается на каждом рендере, независимо от причины. В этом заключается его главное отличие от UNSAFE_componentWillReceiveProps. Он запускается только тогда, когда родительский класс вызывает re-render, а не в результате локального setState.

Мы сравниваем nextProps.someValue с this.props.someValue. Если они разные, мы выполняем операцию setState.

static getDerivedStateFromProps(nextProps, prevState){   if(nextProps.someValue!==prevState.someValue){     
   return { someState: nextProps.someValue};  
} else return null;}

Он получает параметры (params) nextProps и prevState. Как упоминалось ранее, вы не можете получить доступ к this внутри этого метода. Вам придется хранить свойства (props) в таком состоянии, чтобы можно было сравнить nextProps с предыдущими свойствами. В приведенном выше коде сравниваются nextProps и prevState. Если они оба разные, объект будет возвращен для обновления состояния. В противном случае будет возвращено значение null,указывающее на то, что обновление состояния не требуется. Если состояние изменяется, вызывается componentDidUpdate, где мы можем выполнить все требуемые операции, как это было в componentWillReceiveProps.

Бонус: Жизненный цикл React компонента

 

Перевод статьи Harsh makadiaWhy React16 is a blessing to React developers