- PVSM.RU - https://www.pvsm.ru -
Автор статьи, перевод которой мы публикуем, считает, что, к несчастью, в большинстве из существующих руководств по React не уделяется должного внимания ценным практическим приёмам разработки. Такие руководства не всегда дают тому, кто по ним занимается, понимание того, что такое «правильный подход» к работе с React.
В этом руководстве, которое рассчитано на начинающих разработчиков, имеющих знания в области HTML, JavaScript и CSS, будут рассмотрены основы React и самые распространённые ошибки, с которыми может столкнуться программист, пользующийся данной библиотекой.
Прежде чем мы приступим к делу, скажем пару слов о том, почему React можно считать наилучшей альтернативой среди средств для разработки веб-интерфейсов. Существует множество UI-фреймворков. Почему стоит выбрать именно React? Для того чтобы ответить на этот вопрос — сравним два самых популярных инструмента для разработки интерфейсов — React и Angular. Надо отметить, что в это сравнение можно было бы включить и набирающий популярность фреймворк Vue.js, но мы ограничимся React и Angular.
React-разработка заключается в описании того, что нужно вывести на страницу (а не в составлении инструкций для браузера, посвящённых тому, как это делать). Это, кроме прочего, означает значительное сокращение объёмов шаблонного кода.
В составе Angular, с другой стороны, есть средства командной строки, которые генерируют шаблонный код компонентов. Не кажется ли это немного не тем, чего можно ждать от современных инструментов разработки интерфейсов? Фактически, речь идёт о том, что в Angular так много шаблонного кода, что для того, чтобы его генерировать, даже создано специальное средство.
В React, приступая к разработке, просто начинают писать код. Тут нет шаблонного кода компонентов, который нужно как-то генерировать. Конечно, перед разработкой нужна некоторая подготовка, но, когда дело доходит до компонентов, их можно описывать в виде чистых функций.
В Angular-коде используются директивы вроде ng-model
, ngIf
и ngFor
. Выглядит такой код довольно-таки громоздко. В React, с другой стороны, применяется синтаксис JSX, который воспринимается как обычный HTML, то есть, для того, чтобы приступить к React-разработке, не нужно изучать принципиально новые вещи. Вот как это выглядит:
const Greetings = ({ firstName }) => (
<div>Hi, {firstName}</div>
);
Кривая обучения — это важный фактор, который нужно учитывать при выборе UI-фреймворка. В этой связи надо отметить, что в React имеется меньше абстракций, чем в Angular. Если вы знаете JavaScript, то, вероятно, вы сможете научиться писать React-приложения буквально за день. Конечно, для того, чтобы научиться делать это правильно, потребуется некоторое время, но приступить к работе можно очень и очень быстро.
Если же проанализировать Angular, то окажется, что если вы решите освоить этот фреймворк, вам придётся изучить новый язык (Angular использует TypeScript), а также научиться использовать средства командной строки Angular и привыкнуть к работе с директивами.
В Angular имеется система двусторонней привязки данных. Это, например, выражается в том, что изменения в форме элемента приводят к автоматическому обновлению состояния приложения. Это усложняет отладку и является большим минусом данного фреймворка. При таком подходе, если что-то идёт не так, программист не может совершенно точно знать о том, что именно стало причиной изменения состояния приложения.
В React, с другой стороны, используется односторонняя привязка данных. Это — большой плюс данной библиотеки, так как выражается это в том, что программист всегда точно знает о том, что привело к изменению состояния приложения. Подобный подход к привязке данных значительно упрощает отладку приложений.
Я полагаю, что одной из сильнейших сторон React является тот факт, что эта библиотека не принуждает разработчика к использованию классов. В Angular же все компоненты должны быть реализованы в виде классов. Это приводит к чрезмерному усложнению кода, не давая никаких преимуществ.
В React все компоненты пользовательского интерфейса могут быть выражены в виде наборов чистых функций. Использование чистых функций для формирования UI можно сравнить с глотком чистого воздуха.
Теперь, когда мы рассмотрели причины популярности React, которые, вполне возможно, склонят вас в сторону именно этой библиотеки при выборе инструментов для разработки пользовательских интерфейсов, перейдём к практике.
Node.js — это серверная платформа, поддерживающая выполнение JavaScript-кода, возможности которой пригодятся нам для React-разработки. Если эта платформа ещё у вас не установлена — сейчас самое время это исправить [2].
Здесь мы, для создания основы React-приложения, будем использовать пакет create-react-app
от Facebook. Вероятно, это самый популярный подход к настройке рабочего окружения, которое позволяет приступить к разработке. Благодаря create-react-app
программист получает в своё распоряжение множество нужных инструментов, что избавляет его от необходимости самостоятельно их подбирать.
Для того чтобы глобально установить create-react-app
, воспользуйтесь такой командой:
npm i -g create-react-app
Затем, для создания шаблона приложения, выполните такую команду:
create-react-app react-intro
На этом предварительная подготовка закончена. Для запуска приложения выполните следующие команды:
cd react-intro
npm start
Тут мы переходим в папку проекта и запускаем сервер разработки, который позволяет открыть новое React-приложение, перейдя в браузере по адресу http://localhost:3000/ [3].
Разберёмся теперь с тем, как устроено React-приложение. Для этого откройте только что созданный проект с помощью вашей IDE (я рекомендую Visual Studio Code [4]).
Находясь в папке проекта, откройте файл, который находится по адресу public/index.html
. Вот что вы увидите, сделав это.
Файл index.html
Здесь нас особенно интересует строка . Именно тут будет находиться наше React-приложение. Весь этот элемент будет заменён на код приложения, а всё остальное останется неизменным.
Теперь откроем файл src/index.js
. Именно этот файл выполняет развёртывание React-приложения. И, между прочим, исходный код приложения будет размещаться в директории src
.
Файл index.js
Вот строка кода, которая ответственна за вывод того, что мы называем «React-приложением», на страницу:
ReactDOM.render(<App />, document.getElementById('root'));
Эта строка сообщает React о том, что нужно взять компонент App
(совсем скоро мы о нём поговорим) и поместить его в div
-элемент root
, который был определён в только что рассмотренном нами файле index.html
.
Разберёмся теперь с конструкцией <App />
. Она очень похожа на HTML-код, но это — образец JSX-кода, который представляет собой особый синтаксис JavaScript, используемый React. Обратите внимание на то, что эта конструкция начинается с заглавной буквы A
, на то, что это именно <App />
, а не <app />
. Это так из-за используемого в React соглашения по именованию сущностей. Такой подход позволяет системе различать обычные HTML-теги и компоненты React. Если имена компонентов не будут начинаться с заглавной буквы, React не сможет вывести их на страницу.
Если в некоем .js
-файле планируется использовать JSX, там необходимо импортировать React, воспользовавшись следующей командой:
import React from 'react';
Теперь мы готовы к тому, чтобы взглянуть на код нашего первого компонента. Для этого откроем файл src/App.js
.
Файл App.js
Для того чтобы создать компонент React, необходимо сначала создать класс, являющийся наследником React.Component
. Именно эту задачу решает строка class App extends Component
. Все компоненты React должны содержать реализацию метода render
, в котором, как можно догадаться из его названия, выполняется рендеринг компонента, формирование описания его визуального представления. Этот метод должен вернуть HTML-разметку для вывода её на страницу.
Обратите внимание на то, что атрибут className
— это эквивалент атрибута class
в HTML. Он используется для назначения элементам CSS-классов в целях их стилизации. Ключевое слово class
в JavaScript является зарезервированным, его нельзя использовать в качестве имени атрибута.
Повторим то, что мы только что выяснили о компонентах:
A
в App
).React.Component
.render
, возвращающий разметку.Теперь поговорим о том, чего, при разработке React-приложений, стоит избегать.
Компоненты в React можно создавать, применяя два подхода. Первый заключается в использовании классов компонентов (Class Component), второй — в использовании функциональных компонентов (Functional Component). Как вы, возможно, заметили, в вышеприведённом примере используются классы. К сожалению, большинство руководств по React для начинающих предлагают использовать именно их.
Что плохого в описании компонентов с использованием механизма классов? Дело в том, что такие компоненты тяжело тестировать и они имеют свойство чрезмерно разрастаться. Эти компоненты подвержены проблеме некачественного разделения ответственности, смешиванию логики и визуального представления (а это усложняет отладку и тестирование приложений). В целом, использование классов компонентов ведёт к тому, что программист, фигурально выражаясь, «стреляет себе в ногу». Поэтому, особенно если речь идёт о начинающих программистах, я порекомендовал бы им совсем не пользоваться классами компонентов.
Итак, мы выяснили, что применение классов для описания компонентов — это не лучшая идея. Какие же у нас есть альтернативы? Ответом на этот вопрос являются функциональные компоненты. Если в компоненте, созданном с использованием класса, нет ничего кроме метода render
, то он является отличным претендентом на переработку его в функциональный компонент. Вооружившись этой идеей, подумаем о том, как улучшить компонент App
, созданный средством create-react-app
:
function App() {
return (
<div className="App">
...
</div>
);
}
export default App;
Видите, что мы тут сделали? А именно, мы убрали класс и заменили метод render
конструкцией вида function App() {...}
. Если же тут воспользоваться синтаксисом стрелочных функций ES6, то наш код будет выглядеть ещё лучше:
const App = () => (
<div className="App">
...
</div>
);
export default App;
Мы превратили класс в функцию, возвращающую разметку, которую надо вывести на страницу.
Поразмыслите над этим. В функции, которая возвращает разметку, нет шаблонного кода. Это — практически чистая разметка. Разве это не прекрасно?
Код функциональных компонентов гораздо легче читать, работая с ними, приходится гораздо меньше отвлекаться на стандартные конструкции.
Тут надо отметить, что хотя только что мы сказали о том, что функциональные компоненты предпочтительнее классов компонентов, в этом материале мы будем пользоваться, в основном, классами, так как код классов компонентов оказывается понятнее для новичков, он полагается на меньшее количество абстракций, с его помощью легче демонстрировать ключевые концепции React. Но когда вы в достаточной мере освоитесь в деле разработки React-приложений, настоятельно рекомендуется учитывать, при разработке реальных проектов, то, что было сказано выше. Для того чтобы лучше разобраться с функциональными компонентами — взгляните на этот материал [5].
Свойства (props) — это одна из центральных концепций React. Что такое «свойства»? Для того чтобы это понять, вспомните о параметрах, которые передают функциям. В сущности, свойства — это и есть параметры, которые передаются компонентам. Рассмотрим следующий код:
const Greetings = (props) => <div>Hey you! {props.firstName} {props.lastName}!</div>;
const App = () => (
<div>
<Greetings firstName="John" lastName="Smith" />
</div>
);
Тут мы создали компонент Greetings
и воспользовались им для того, чтобы поприветствовать человека, которого зовут John Smith
, из компонента App
. Весь этот код приведёт к формированию следующей HTML-разметки:
<div>
<div>Hey you! John Smith!</div>
</div>
Фигурные скобки в выражениях вроде {props.name}
используются для выделения JavaScript-кода. Компоненту Greetings
передаются, в виде параметров, свойства firstName
и lastName
. Мы работаем с ними, обращаясь к свойствам объекта props
.
Обратите внимание на то, что компоненту передаётся единственный объект props
, а не два значения, представляющие свойства firstName
и lastName
.
Код можно упростить, воспользовавшись возможностями ES6 по деструктурированию объектов:
const Greetings = ({ firstName, lastName }) => <div>Hey you! {firstName} {lastName}!</div>;
Как видите, тут конструкция (props)
была заменена на ({ firstName, lastName })
. Этой заменой мы сообщаем системе о том, что нас интересуют лишь два свойства объекта props
. И это, в свою очередь, позволяет нам напрямую обращаться к значениям firstName
и lastName
, не указывая в явном виде свойства объекта наподобие props.firstName
.
Что если для решения такой же задачи мы, вместо функциональных компонентов, использовали бы компоненты, основанные на классах? В таком случае код компонента Greetings
выглядел бы так:
class Greetings extends React.Component {
render() {
return (
<div>Hey you! {this.props.firstName} {this.props.lastName}!</div>
);
}
}
Не знаю, какие ощущения подобный код вызывает у вас, но мне он кажется перегруженным вспомогательными механизмами. Здесь, в частности, для доступа к свойствам, необходимо пользоваться конструкциями вида this.props
.
Принцип единственной ответственности (Single Responsibility Principle, SRP) — это один из важнейших принципов программирования, которого следует придерживаться. Он говорит нам о том, что модуль должен решать только одну задачу и должен делать это качественно. Если разрабатывать проект, не следуя только одному этому принципу, код такого проекта может превратиться в кошмарную конструкцию, которую невозможно поддерживать.
Как можно нарушить принцип единственной ответственности? Чаще всего это происходит тогда, когда несвязанные друг с другом механизмы размещают в одних и тех же файлах. В этом материале мы часто будем обращаться к данному принципу.
Новички обычно размещают в одном файле множество компонентов. Например, у нас код компонентов Greetings
и App
находится в одном файле. На практике так поступать не следует, так как это нарушает SRP.
Даже очень маленькие компоненты (вроде нашего компонента Greetings
) нужно размещать в отдельных файлах.
Поместим код компонента Greetings
в отдельный файл:
import React from "react";
const Greetings = ({ firstName, lastName }) => (
<div>
Hey you! {firstName} {lastName}!
</div>
);
export default Greetings;
Затем воспользуемся этим компонентом в компоненте App
:
import Greetings from "./Greetings";
const App = () => (
...
);
Обратите внимание на то, что имя файла должно совпадать с именем компонента. То есть, код компонента App
должен размещаться в файле App.js
, код компонента Greetings
— в файле Greetings.js
, и так далее.
Состояние (state) — это ещё одна из центральных концепций React. Именно здесь хранятся данные приложения — то есть то, что может меняться. Нужно сохранить что-то, введённое в поле формы? Используйте состояние. Нужно сохранить очки, набранные игроком в браузерной игре? Для этого тоже надо использовать состояние приложения.
Создадим простую форму, с помощью которой пользователь может ввести своё имя. Обратите внимание на то, что здесь я намеренно использую для описания компонента класс, так как это облегчает демонстрацию рассматриваемой концепции. О том, как преобразовать компонент, созданный с использованием класса, в функциональный компонент, можете почитать здесь [5].
import React from "react";
class SimpleForm extends React.Component {
render() {
return (
<div>
<input type="text" name="firstName" />
<Greetings firstName="John" />
</div>
);
}
}
const App = () => (
<div>
<SimpleForm />
</div>
);
В поле формы пользователь может что-то ввести, и это хорошо. Однако если вы внимательно прочли этот код, то вы можете заметить, что в качестве имени пользователя, в выводимом на странице приветствии, всегда используется John
. Что если так зовут далеко не всех наших пользователей? Если это так, то мы ставим себя в не очень-то удобное положение.
Как использовать значение, введённое в поле? В React не полагается обращаться к элементам DOM напрямую. Решить эту задачу нам помогут обработчики событий и состояние приложения.
class SimpleForm extends React.Component {
state = {
firstName: "",
};
onFirstNameChange = event =>
this.setState({
firstName: event.target.value
});
render() {
return (
<div>
<input type="text" name="firstName" onChange={this.onFirstNameChange} />
<Greetings firstName={this.state.firstName} />
</div>
);
}
}
Состояние можно представить себе как простой JavaScript-объект, который, в виде свойства, хранится в классе компонента SimpleForm
. В этот объект мы добавляем свойство firstName
.
Тут мы оснастили поле firstName
обработчиком события. Он запускается каждый раз когда пользователь вводит хотя бы один символ в поле. В классе за обработку события onChange
отвечает свойство onFirstNameChange
, в представляющей которое функции выполняется команда следующего вида:
this.setState(...)
Именно здесь выполняется обновление состояния компонента. Состояние компонентов не обновляют напрямую. Это делается только с использованием метода setState
. Для того чтобы записать новое значение в свойство firstName
, мы просто передаём этому методу объект, содержащий то, что нужно записать в состояние:
{ firstName: event.target.value }
В данном случае event.target.value
— это то, что пользователь ввёл в поле формы, а именно — его имя.
Обратите внимание на то, что мы не объявили onFirstNameChange
в виде метода. Очень важно, чтобы подобные вещи объявлялись бы в виде свойств класса, содержащих стрелочные функции, а не в виде методов. Если объявить подобную функцию в виде метода, тогда this
будет привязано к элементу формы, который вызывает этот метод, а не к классу, как мы могли бы ожидать. Эта мелочь часто сбивает с толку новичков. Это — одна из причин рекомендации по использованию функциональных компонентов, а не классов компонентов.
Реализуем простую систему проверки данных, введённых в форму, используя регулярные выражения. Давайте решим, что имя должно состоять как минимум из трёх символов и может содержать лишь буквы.
Добавим в компонент обработчик события onBlur
, который вызывается в тот момент, когда пользователь покидает поле ввода. В состояние приложения добавим ещё одно свойство — firstNameError
. Если при вводе имени возникла ошибка, будем выводить сообщение об этом под полем.
Разберём этот код.
class SimpleForm extends React.Component {
state = {
firstName: "",
firstNameError: "",
};
validateName = name => {
const regex = /[A-Za-z]{3,}/;
return !regex.test(name)
? "The name must contain at least three letters. Numbers and special characters are not allowed."
: "";
};
onFirstNameBlur = () => {
const { firstName } = this.state;
const firstNameError = this.validateName( firstName );
return this.setState({ firstNameError });
};
onFirstNameChange = event =>
this.setState({
firstName: event.target.value
});
render() {
const { firstNameError, firstName } = this.state;
return (
<div>
<div>
<label>
First name:
<input
type="text"
name="firstName"
onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
/>
{firstNameError && <div>{firstNameError}</div>}
</label>
</div>
<Greetings
firstName={firstName}
/>
</div>
);
}
}
Разрабатывая систему проверки ввода, мы, для начала, добавили в состояние новое свойство: firstNameError
:
state = {
...
firstNameError: "",
};
Проверка данных выполняется в стрелочной функции validateName
. Она проверяет введённое имя с помощью регулярного выражения:
validateName = name => {
const regex = /[A-Za-z]{3,}/;
return !regex.test(name)
? "The name must contain at least three letters..."
: "";
}
Если проверка не удалась — мы возвращаем из функции сообщение об ошибке. Если имя прошло проверку — возвращаем пустую строку, которая указывает на то, что ошибок при проверке имени не найдено. Тут мы, ради краткости кода, пользуемся тернарным оператором JavaScript.
Взглянем на обработчик события onBlur
, который вызывается когда пользователь покидает поле ввода:
onFirstNameBlur = () => {
const { firstName } = this.state;
const firstNameError = this.validateName( firstName );
return this.setState({ firstNameError });
};
Здесь мы извлекаем из состояния свойство firstName
, пользуясь возможностями ES6 по деструктурированию объектов. Первая строка этого кода эквивалентна такой:
const firstName = this.state.firstName;
Затем мы вызываем вышеописанную функцию проверки данных, передавая ей firstName
, и записываем в свойство состояния firstNameError
то, что возвратит эта функция. Если проверка не удалась, в это свойство попадёт сообщение об ошибке. Если удалась — туда будет записана пустая строка.
Рассмотрим метод компонента render()
:
render() {
const { firstNameError, firstName} = this.state;
...
}
Тут мы снова пользуемся деструктурирующим присваиванием для извлечения данных из состояния.
<input
...
onBlur={this.onFirstNameBlur}
/>
Эта строка назначает функцию onFirstNameBlur
обработчиком события onBlur
.
{firstNameError && <div>{firstNameError}</div>}
Здесь мы пользуемся особенностями вычисления логических выражений в JavaScript. Элемент div
, содержащий сообщение об ошибке, будет выведен лишь в том случае, если значение firstNameError
может быть приведено к true
.
Если вы воспроизводили у себя то, о чём мы тут говорили, то вы могли заметить, что наша форма выглядит не слишком симпатично. Давайте это исправим, воспользовавшись встроенными стилями:
render() {
const { firstNameError, firstName } = this.state;
return (
<div
style={{
margin: 50,
padding: 10,
width: 300,
border: "1px solid black",
backgroundColor: "black",
color: "white"
}}
>
<div style={{marginBottom: 10}}>
<label>
First name:
<input
style={{backgroundColor: '#EFEFFF', marginLeft: 10}}
type="text"
name="firstName"
onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
/>
{firstNameError && <div style={{color: 'red', margin: 5}}>{firstNameError}</div>}
</label>
</div>
<Greetings
firstName={firstName}
/>
</div>
);
}
Стилизация компонентов в React выполняется путём передачи стилей в атрибут style
.
Признаю, что я не дизайнер, но то, что у меня получилось, выглядит сейчас гораздо лучше, чем прежде. Вот какой стала теперь форма, в которой выводится сообщение об ошибке.
Стилизованная форма с сообщением об ошибке
Тут мы снова сталкиваемся с примером того, чего лучше не делать, разрабатывая React-приложения. Проблема, о которой идёт речь, то есть — размещение стилей в методе render
, к сожалению, распространена чрезвычайно сильно. Почему это плохо? Дело в том, что это нарушает принцип единственной ответственности. Кроме того, стили загромождают код компонентов, что значительно ухудшает читабельность программы.
Как этого избежать? На самом деле, довольно просто. Достаточно создать специальный объект style
, который и будет содержать стили. Рекомендуется размещать стили в отдельном файле. Например, вот код подобного файла с именем style.js
:
// style.js:
const style = {
form: {
margin: 50,
padding: 10,
width: 300,
border: "1px solid black",
backgroundColor: "black",
color: "white"
},
inputGroup: {
marginBottom: 10
},
input: {
backgroundColor: "#EFEFFF",
marginLeft: 10
},
error: {
color: "red",
margin: 5
}
};
export default style;
После того, как этот файл создан, подключим его в компоненте SimpleComponent
:
import style from './style';
class SimpleForm extends React.Component {
...
render() {
const { firstNameError, firstName } = this.state;
return (
<div style={style.form}>
<div style={style.inputGroup}>
<label>
First name:
<input
style={style.input}
type="text"
name="firstName"
onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
/>
{firstNameError && (
<div style={style.error}>{firstNameError}</div>
)}
</label>
</div>
<Greetings firstName={firstName} />
</div>
);
}
}
export default SimpleForm;
Этот код выглядит куда чище, чем его предыдущий вариант. Поэтому примите себе за правило размещать стили в отдельных файлах.
Сделаем нашу форму немного интереснее, добавив в неё поле для ввода фамилии пользователя:
class SimpleForm extends React.Component {
state = {
...
lastName: "",
lastNameError: ""
};
validateName = ...;
onFirstNameBlur = ...;
onFirstNameChange = ...;
onLastNameBlur = () => {
const { lastName } = this.state;
const lastNameError = this.validateName(lastName);
return this.setState({ lastNameError });
};
onLastNameChange = event =>
this.setState({
lastName: event.target.value
});
render() {
const { firstNameError, firstName, lastName, lastNameError } = this.state;
return (
<div style={style.form}>
<div style={style.inputGroup}>
<label>
First name:
<input
style={style.input}
type="text"
name="firstName"
onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
/>
{firstNameError && <div style={style.error}>{firstNameError}</div>}
</label>
</div>
<div style={style.inputGroup}>
<label>
Last name:
<input
style={style.input}
type="text"
name="lastName"
onChange={this.onLastNameChange}
onBlur={this.onLastNameBlur}
/>
{lastNameError && <div style={style.error}>{lastNameError}</div>}
</label>
</div>
<Greetings firstName={firstName} lastName={lastName} />
</div>
);
}
}
export default SimpleForm;
В обновлённом компоненте не так уж и много изменений — мы просто скопировали код, используемый для описания firstName
и создали копии обработчиков событий.
Неужто это я написал слово «скопировали»? Копирование кода — это то, чего стоит всеми силами избегать.
Монолитный код, не разделённый на части, это проблема, которая, как и многие другие, нарушает принцип единственной ответственности. Хорошо написанный код должен читаться как поэма, а я готов поспорить, что код метода render
нашего компонента выглядит куда хуже. Займёмся решением этой проблемы.
Поля ввода, используемые в форме, практически идентичны, и тому и другому нужны некие механизмы проверки данных. Применим к нашему компоненту некоторые приёмы рефакторинга и создадим компонент TextField
, подходящий для повторного использования.
import React from 'react'
import style from "./style";
const TextField = ({name, onChange, onBlur, error, label}) => (
<div style={style.inputGroup}>
<label>
{label}
<input
style={style.input}
type="text"
name={name}
onChange={onChange}
onBlur={onBlur}
/>
{error && <div style={style.error}>{error}</div>}
</label>
</div>
);
export default TextField;
Я, создавая этот компонент, просто извлёк код одного из полей ввода из метода render
и оформил его в виде функционального компонента. То, что в разных экземплярах подобного компонента может меняться, передано ему в виде свойств.
Вот как можно использовать этот новый компонент в компоненте SimpleForm
:
...
import TextField from './TextField';
class SimpleForm extends React.Component {
...
render() {
const { firstNameError, firstName, lastName, lastNameError } = this.state;
return (
<div style={style.form}>
<TextField name="firstName"
label="First name:"
onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
error={firstNameError} />
<TextField name="lastName"
label="Last name:"
onChange={this.onLastNameChange}
onBlur={this.onLastNameBlur}
error={lastNameError} />
<Greetings firstName={firstName} lastName={lastName} />
</div>
);
}
}
Такой код читать легче, чем прежний. Мы можем пойти ещё дальше, создав отдельные компоненты TextField
для имени и фамилии. Вот код компонента FirstNameField
:
import React from 'react';
import TextField from './TextField';
const FirstNameField = ({...rest}) => (
<TextField name="firstName"
label="First name:"
{...rest} />
);
export default FirstNameField;
Тут мы просто возвращаем компонент, заранее подготовленный для вывода имени пользователя. В конструкции ({...rest})
используется синтаксис оставшихся параметров (оператор rest, выглядящий как три точки). Благодаря использованию этого оператора оказывается, что всё, что будет передано компоненту в виде свойств, попадёт в объект rest
. Затем, для того, чтобы передать свойства компоненту TextField
, используется так называемый «оператор расширения», или оператор spread (он, в конструкции {...rest}
, тоже выглядит как три точки). Тут берётся объект rest
, из него выделяются его свойства, которые передаются компоненту TextField
.
Другими словами, все эти конструкции позволяют выразить следующую идею: мы хотим взять всё, что пришло в компонент FirstNameField
и передать это в неизменном виде компоненту TextField
.
Похожим образом устроен и компонент LastNameField
:
Вот каким теперь будет код формы:
...
import FirstNameField from './FirstNameField';
import LastNameField from './LastNameField';
class SimpleForm extends React.Component {
...
render() {
const { firstNameError, firstName, lastName, lastNameError } = this.state;
return (
<div style={style.form}>
<FirstNameField onChange={this.onFirstNameChange}
onBlur={this.onFirstNameBlur}
error={firstNameError} />
<LastNameField onChange={this.onLastNameChange}
onBlur={this.onLastNameBlur}
error={lastNameError} />
<Greetings firstName={firstName} lastName={lastName} />
</div>
);
}
}
Теперь код компонента выглядит гораздо лучше.
Перечислим причины, по которым не рекомендуется использовать компоненты, основанные на классах, отдавая предпочтение функциональным компонентам:
this
, которое всегда являлось источником путаницы.Хотя этот материал и получился достаточно длинным, о создании React-приложений можно говорить ещё очень долго. В частности, о том, как правильно организовывать код, и о том, какие приёмы разработки рекомендуется и не рекомендуется использовать. Несмотря на то, что это руководство не является всеобъемлющим, мы полагаем, что если благодаря ему состоялось ваше первое знакомство с React, то вы, пользуясь тем, что сегодня узнали, сможете эффективно осваивать эту UI-библиотеку.
→ Исходный код примеров, рассмотренных в этом материале, можно найти здесь [6]
Уважаемые читатели! Если сегодня, в ходе прочтения этой статьи, вы написали свои первые строки React-кода, просим поделиться впечатлениями.
Автор: ru_vds
Источник [7]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/javascript/297353
Ссылки в тексте:
[1] Image: https://habr.com/company/ruvds/blog/428077/
[2] исправить: https://nodejs.org/en/download/
[3] http://localhost:3000/: http://localhost:3000/
[4] Visual Studio Code: https://code.visualstudio.com/
[5] этот материал: https://medium.com/@ilyasz/mastering-react-functional-components-with-recompose-d4dd6ac98834
[6] здесь: https://github.com/suzdalnitski/medium-intro-react
[7] Источник: https://habr.com/post/428077/?utm_source=habrahabr&utm_medium=rss&utm_campaign=428077
Нажмите здесь для печати.