React для начинающих: какие компоненты лучше классы или функции?
React JS это JavaScript-библиотека с открытым исходным кодом, создающая виртуальную DOM поверх стандартной DOM веб-страницы, что предоставляет инновационный подход к управлению состояниями веб-приложения. В React Native логика по сути та же, но специфичная для разработки приложений на мобильных платформах.
Чтобы освоить React Native, вам придется изучить React JS, а для изучения React JS необходимы предварительные знания HTML, CSS и JavaScript.
У React JS и React Native есть конкуренты?
Да! Для создания мобильных и веб-приложений прекрасно подходят несколько других библиотек или фреймворков, например, Flutter и Angular.
Почему React так называется?
В React приложения создаются на основе компонентного подхода: пользовательский интерфейс разделяется на отдельные и независимые компоненты, в дальнейшем собирающиеся в интерфейсы любой сложности.
Компоненты состоят из JavaScript-класса, состояния и метода рендеринга:
class Post extends Component {
constructor(props){
super(props);
this.state = {}
}
render(){
// код пользовательского интерфейса
}
}
Состояние это данные, отображаемые при отрисовке компонента методом render()
. Обычно данный метод содержит HTML или JSX код.
Метод render()
возвращает JavaScript-объект, связанный с элементом DOM веб-страницы. React хранит в памяти облегченное представление DOM, для простоты оно называется “виртуальный DOM”.
Таким образом, при каждом изменении состояния компонента создаётся новый элемент React. Затем, в виртуальной DOM, React сравнивает этот новый элемент с реальной DOM и синхронизирует их. Таким образом, больше не нужно работать с DOM API в браузере, как в ванильном JavaScript или JQuery.
Следовательно, именно поэтому React называется “React” он обнаруживает изменения в виртуальном DOM, сравнивая его с реальным DOM, и реагирует на них, обновляя реальный DOM соответственно.
Противостояние
Поскольку в последнее время противостояние компонентов-классов и компонентов-функций стало горячей темой в конкурентных дебатах среди сообщества React-разработчиков, многие из них еще не определились, какие компоненты лучше.
Поэтому разобраться вам поможет сравнение, подчеркивающее плюсы и минусы обоих компонентов по указанному ниже набору критериев.
Синтаксис
У компонентов-функций и компонентов-классов синтаксис написания совершенно разный: компоненты-функции похожи на простые JavaScript-функции, а компоненты-классы, соответственно, похожи на обычные JavaScript-классы.
Компоненты-функции это буквально простые JavaScript-функции, как показано выше: компонент отображает код пользовательского интерфейса, зачастую выражение JSX или необработанный HTML-код. В примере написан действительный компонент React, потому что в него можно передавать свойства props
. Пример компонента-функции:
export default function Welcome(props) { return( // код пользовательского интерфейса )}
Компоненты-классы же пишутся на основе обычных классов ES6 JavaScript, как показано выше. Метод render()
отрисовывает указанный внутри него пользовательский интерфейс.
Передача свойств
В компонентах-классах и функциональных компонентах работа со свойствами отличается. Пример:
class Time extends React.Component {
constructor(props) {
super(props);
this.state = {date: date: new Date()};
}
render() {
return (
<div>
<h1>It is {this.state.date.toLocaleTimeString()}</h1>
</div>
);
}
}
Компоненты-классы обычные классы ES6 JavaScript, поэтому свойства передаются в конструктор, как в примере выше. Доступ к свойствам в компоненте-классе получается следующим выражением: {this.props.<propertyName>}
.
function Welcome(props) { return <h1>Hello, {props.name}</h1>;}
Функциональные компоненты принимают props
в качестве единственного аргумента, поэтому вы можете всегда передавать свойства как props
. Но если вы хотите передавать сложные свойства, то потребуется деструктурирующее присваивание.
В примере передаётся объект user
со свойствами title
и name
:
function Welcome({user}) { return <h1>Hello, <h1>Hello , {user.title, " ", user.name}</h1>;}
Поскольку свойства уже деструктурированы, можно просто получить к ним доступ в любом месте функции через propName.attribute
.
Передача свойств компонентам, как классам, так и функциям, описывается так:
<Welcome name = "John" />
Управление состоянием
Как было сказано выше, управление состоянием одна из основных фишек React: компоненты-классы управляют сложными объектами состояния, а конструктор внутри класса не только принимает свойства, но и присваивает начальное состояние this.state
.
class Welcome extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
render() {
return (
<div>
<h1>Hello, world!</h1>
</div>
);
}
}
Метод-конструктор назначает начальное состояние класса, другими словами, распределяет состояние по компоненту-классу; также конструктор принимает свойства.
Компоненты-функции полезны в случаях, когда не требуется детальный контроль состояния.
Однако, когда вы пишете компонент-функцию и вдруг понимаете, что вам нужно добавить состояние, вам не обязательно преобразовывать весь компонент в компонент класса. Для этого прекрасно подойдёт хук React useState()
, представленный ниже:
import React, { useState } from 'react';
function Counter() {
// Определение переменной-счётчика
const [count, setCount] = useState(0);
return (
<div>
<p>Вы нажали {count} раз</p>
<button onClick={() => setCount(count + 1)}>
Нажми меня
</button>
</div>
);
}
Приведенный выше компонент Counter
отображает количество нажатий на кнопку.
Хук useState
импортируется из React
и состоит из трех основных элементов:
- Переменная состояния.
- Функция, обновляющая состояние.
- Начальная переменная состояния.
Методы жизненного цикла
Компоненты-классы реализуют следующие методы жизненного цикла:
componentDidMount()
метод срабатывает сразу после монтирования компонента. Идеальным примером послужит загрузка данных изfetch
вызова API на странице архива.
Данный метод идеально подходит для настройки начального состояния, однако впоследствии, после размонтирования компонента, метод необходимо отменить.componentDidUpdate()
метод не вызывается на этапе начальной отрисовки, однако он вызывается каждый раз, когда компонент обновляется, а состояние или свойства изменяются. Логика метода всегда оборачивается условиемif
для сравнения обновленных свойств с предыдущими.componentWillUnmount()
метод срабатывает непосредственно перед тем, размонтированием и уничтожением компонента: он очищает любой “мусор”, например, аннулирует таймеры, отменяет сетевые запросы или любые подписки. Таким образом, предотвращается утечка памяти.
Хуки это новое дополнение к React 16.8. Хук React useEffect()
похож на componentDidMount()
, componentDidUpdate()
и componentWillUnmount()
вместе взятые. Пример:
import React, {useState, useEffect} from 'react';
function Example() {
const [count, setCount] = useState(0);
useEffect(() => {
document.title = `Нажато ${count} раз`;
});
}
Производительность
Хотя компоненты-функции это обычные функции JavaScript, а компоненты-классы, соответственно, базируются на классах JavaScript, никем не было доказано, что компоненты-функции выполняются быстрее компонентов-классов.
Тем не менее, компоненты-функции многими программистами считаются более легкими, поскольку в них требуется компилировать гораздо меньше кода, чем в компонентах-классах. Кроме того, если говорить о меньшем количестве кода, то его сравнительно легче читать и тестировать.
Тестирование
Компоненты-функции легче тестировать, чем компоненты-классы, поскольку это просто JavaScript-функции; написание тестовых примеров и выполнение тестов намного удобнее, чем для компонентов-классов.
Для выполнения тестов рекомендуется фреймворк Jest, однако React Testing Library также прекрасно справится; обе библиотеки работают по-разному.
Всё-таки компонент-класс или компонент-функция?
Применение только компонентов-функций это следование процедурному программированию, однако компоненты-классы это следование объектно-ориентированной парадигме, следовательно, компоненты-функции могут показаться гораздо более простыми в реализации.
И компоненты классов, и функциональные компоненты имеют свои преимущества, уже рассмотренные в данном руководстве, а некоторые программисты предвзяты в своем решении. Итак, что же выбрать?
Задайтесь простым вопросом: компонент хранит какое-либо состояние? Если да, то реализуйте его с помощью компонента-класса; в противном случае, реализуйте его в виде компонента-функции.