Практически каждый рад, когда выходит новая версия любимого мобильного приложения или 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! :D
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
может быть соединен со многими Consumer
. Provider
могут быть вложенными для предопределения значений глубже в дереве.
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 makadia: Why React16 is a blessing to React developers