Введение React - это js-библиотека для разработки UI (User Interface - пользовательский интерфейс) преимущества постепенное внедрение инструменты React — это JavaScript-библиотека для разработки пользовательского интерфейса Декларативный Создавать интерактивные пользовательские интерфейсы на React — приятно и просто. Вам достаточно описать, как части интерфейса приложения выглядят в разных состояниях. React будет своевременно их обновлять, когда данные изменяются. Декларативные представления сделают код более предсказуемым и упростят отладку. Основан на компонентах Создавайте инкапсулированные компоненты с собственным состоянием, а затем объединяйте их в сложные пользовательские интерфейсы. Поскольку логика компонента написана на JavaScript, а не содержится в шаблонах, можно с лёгкостью передавать самые разные данные по всему приложению и держать состояние вне DOM. Научитесь однажды — пишите где угодно Нам не нужно ничего знать про остальную часть вашего технологического стека, поэтому вы можете разрабатывать новую функциональность на React, не изменяя существующий код. React также может работать на Node.js, и даже на мобильных платформах с помощью React Native. React изначально был спроектирован так, чтобы его можно было внедрять постепенно. Другими словами, вы можете начать с малого и использовать только ту функциональность React, которая необходима вам в данный момент. Если вы просто хотите немного поиграть с React, попробуйте онлайн-песочницу. Например, вот простейший шаблон на CodeSandbox или Glitch , а можно работать в CodePen Текстовый редактор для локальной работы можно выбрать произвольный (Visual Studio Code, WebStorm, Sublime, тот_самый_ваш_редактор ) Во многих редакторах есть удобные плагины, в VScode для работы c React могут пригодиться: ES7 React/Redux/GraphQL/React-Native snippets (от dsznajder), Live Server (от Ritwick Dey), JavaScript (ES6) code snippets (от charalampos karypidis) Для быстрого старта можно использовать Create React App Если хотите узнать как настроить сборку файлов React, познакомьтесь c Webpack
Контейнер приложения добавление тегов script !>#root+script*2 Для внедрения React не надо ничего переписывать. Его можно использовать как для маленькой кнопки, так и для целого приложения. Возможно, вы захотите немного «оживить» вашу страницу. React-компоненты подходят для этого как нельзя лучше. Большинство сайтов в Интернете является обычными HTML-страницами. Даже если ваш сайт не относится к одностраничным приложениям, вы можете добавить на него React, написав всего несколько строк кода без каких-либо инструментов сборки. В зависимости от целей, можно постепенно перенести на React весь сайт или переписать всего несколько виджетов Для добавления React нужно создать HTML-элемент контейнер и подключить скрипты. Идентификатор контейнера часто называется root или app , но можно выбрать собственный
Подключение React react react-dom ссылки на CDN development/production Модульная структура React представлен двумя библиотеками: основной - react и react-dom для работы с DOM При разработке рекомендуется использовать версию development - она содержит не минимизированный код с комментариями, а production - нужно использовать когда приложение работает на 'боевом' сайте
Обратите внимание на число, идущее в URL после названия библиотеки - оно обозначает версию React
Практическая работа Первое использование React Упражнение 1 Подключение библиотеки 1. Создайте/откройте файл index.html 2. В файле index.html создайте 3. Подключите в нижней части файла React и Babel
4. Подключите набор данных с описанием книг
5. Создайте блок для работы со скриптом с учётом babel или
6. Откройте страницу в браузере через Go Live (VSCode) или Live Preview (Brackets). Примечание: если в VSCode не установлен Live Server - установите его через расширения 7. Убедитесь, что на странице выводится "Hello, world!" в заголовке первого уровня
Привет, мир const element = React.createElement( 'h1', {className: 'foo'}, 'Привет, мир!' ); ReactDOM.render( element, document.querySelector('#root') ); Для создания и отрисовки React-элемента на странице, воспользуемся методом React.createElement(elem, attrs, content ) elem - название HTML-элемента attrs - объект для атрибутов content - содержимое - текст или другие элементы Можно написать фрагмент и единым целым, но обычно так не делают: ReactDOM.render( React.createElement( 'h1', {className: 'foo'}, 'Привет, мир!' ), document.querySelector('#root') );
Использование JSX const element =
Привет, мир!
; ReactDOM.render( element, document.querySelector('#root') ); Babel JavaScript компилятор конвертация ES6+ в ES5 поддержка JSX попробовать и установить
Установка Babel Для быстрого старта, Babel можно установить тегом script с адресом на CDN
Теги script с файлами компонентов или JSX-кодом должны быть снабжены атрибутом type='text/babel' . Не используйте Babel в production - библиотека занимает много места и так не принято.
Дочерние элементы const element =
Привет, мир!
Ехал Грека
; const element = (
Привет, мир!
Ехал Грека
); Без JSX const element = React.createElement( 'div', null, [ React.createElement('h1', {className: 'foo'}, 'Привет, мир!'), React.createElement('p', null, 'Ехал Грека'), ] ); При размещении композиции элементов нужно следить за тем, что бы у них был общий элемент при отрисовке. Нельзя в одну переменную поместить два фрагмента JSX, не объединив их.
Встраивание выражений const element =
Привет, мир! Сейчас {new Date().toLocaleTimeString()}
; Подобно JavaScript-синтаксису `Привет, ${name}` , в JXS можно встраивать выражения для вывода содержимого и формирования атрибутов Выражения могут содержать вычисления, склейку строк, создание экземпляров объектов. В фигурных скобках можно указывать JavaScript-массивы, содержащие в качестве элементов JSX let names = [
John
,
Василий
, ] ReactDOM.render(
{names}
, document.getElementById("app") );
Определение атрибутов /> В предыдущем пункте было рассказано о создании выражений, но стоит ещё повторить - выражения могут использоваться для создания атрибутов. Пользовательских с data- , классов с помощью className и других. При этом в выражении можно использовать тернарный оператор, методы массивов типа Array.prototype.map или Array.prototype.filter
ReactDOM.render(, document.getElementById('app')); Для встраивания CSS создаются объекты, свойства которых содержат названия CSS-свойств в 'верблюжьей' нотации Можно добавить анимацию: const divStyle = { color: 'white', background: '#369', fontSize: '2rem', animationName: 'test', animationDuration: '3s', animationFillMode: 'forwards' }; const Hello = p =>
Привет, мир!
; ReactDOM.render(, document.getElementById('app')); @keyframes test { from { margin-left: 100%; } to { margin-left: 0%; transform: rotate(-10deg) } }
Обновление DOM function tick() { const element = (
Сейчас: {new Date().toLocaleTimeString()}.
); ReactDOM.render( element, document.getElementById('root') ); } setInterval(tick, 1000); Элементы React иммутабельны . После создания элемента, нельзя изменить его потомков или атрибуты. Элемент похож на кадр в фильме: он отражает состояние интерфейса в конкретный момент времени. Пока что, мы знаем только один способ обновить интерфейс — это создать новый элемент и передать его в ReactDOM.render()
react-motion для решения проблем с вашей анимацией ссылка на CDN ReactMotion let {Motion, spring} = ReactMotion; ReactDOM.render( {value =>
{value.x}
} , document.getElementById('app')); В приведённом выше примере, анимируется счётчик от 0 до 10
Тест пройти тест по основам
Практическая работа Работа с синтаксическим расширением JSX Упражнение 1 Встраивание выражений в JSX 1. Вместо «Hello, world!» вставьте фразу «Book 1» 2. Поменяйте h1 на h3. Убедитесь, что вместо «Hello, world!» теперь выводится «Book 1» 3. Перенесите JSX
Book 1
в константу book 4. Константу book пропишите первым аргументом метода ReactDOM.render() . Убедитесь, что на экране отображается «Book 1» 5. Убедитесь, что доступна константа dataBook 6. Встройте название первой книги в константу book так, чтобы вывод на экране не поменялся: const book =
{dataBook[0]["title"]}
ReactDOM.render( book, document.getElementById('app') ); 7. Измените JSX константы book так, чтобы выводилась также информация об авторе и цене книги. Примечание: автор и цена должны выводится в параграфах с подписями, например Автор: имяАвтора , где имяАвтора – значение поля author 8. Убедитесь, что браузер отображает описание книги. Примечание: все характеристики книги должны быть заключены в обертывающий элемент с классом book 9. Если есть ошибки – исправьте их Упражнение 2 Определение атрибутов 1. Временно добавьте в dataBook новую книгу с полем price равным null dataBook[10] = {id:6, title:"Book 3",author:"Author 3", price: null} 2. Создайте функцию formatPrice(price) , которая при значении price равным null будет ставить прочерк в цене: function formatPrice(price) { return price ? {price} : ; } 3. Выведите временно добавленную книгу из dataBook : const book = (
{dataBook[10]["title"]}
Автор: {dataBook[10]["author"]} Цена: {formatPrice(dataBook[10]["price"])} руб.
); 4. Убедитесь, что для выводимой книги ставится прочерк в цене 5. Добавьте в вывод изображение книги. Примечание: чтобы упростить работу, воспользуйтесь ссылками вида http://placehold.it/100x120 : const book = (
{dataBook[10]["title"]}
Автор: {dataBook[10]["author"]} Цена: {formatPrice(dataBook[10]["price"])} руб.
); 6. Напишите JSX, который выведет информацию обо всех объектах dataBook . Примечание: если будет время
Компоненты и пропсы компоненты функциональные классы пропсы properties Компоненты позволяют разбить интерфейс на независимые части, про которые легко думать в отдельности. Их можно складывать вместе и использовать несколько раз. Во многом компоненты ведут себя как обычные функции JavaScript. Они принимают произвольные входные данные (так называемые «пропсы») и возвращают React-элементы, описывающие, что мы хотим увидеть на экране.
Компонент как функция function Welcome(props) { return
Hello, {props.name}
; } Функции - простой способ объявить React-компонент Название функции начинается с заглавной функции и описывает предназначение компонента. Функция должна возвращать содержимое, определяющее наш строительный кирпичик function Welcome(props) { return
Hello, {props.name}
; } Эта функция — компонент, потому что она получает данные в одном объекте («пропсы») в качестве параметра и возвращает React-элемент. Мы будем называть такие компоненты «функциональными», так как они буквально являются функциями.
Компонент класс class Welcome extends React.Component { render() { return
Hello, {this.props.name}
; } } Компоненты на основе классов позволяют организовывать работу с состоянием - понятием, о котором будем горовить позже Всегда называйте компоненты с заглавной буквы. Если компонент начинается с маленькой буквы, React принимает его за DOM-тег. Например, это div-тег из HTML, а /> это уже наш компонент Welcome , который должен быть в области видимости.
Начальное значение props Welcome.defaultProps = { name: 'гость' } В ситуации, когда пропсы не указаны, иногда имеет смысл указать значения по умолчанию - они будут использованы компонентом Контроль типа данных https://cdnjs.cloudflare.com/ajax/libs/prop-types/15.7.2/prop-types.min.js
Композиция function App() { return (
); } Компоненты могут ссылаться на другие компоненты в возвращённом ими дереве. Это позволяет нам использовать одну и ту же абстракцию — компоненты — на любом уровне нашего приложения. Неважно, пишем ли мы кнопку, форму или целый экран: все они, как правило, представляют собой компоненты в React-приложениях. В приложениях, написанных на React с нуля, как правило, есть один компонент App , который находится на самом верху. В случае, если вы переписываете существующее приложение на React, имеет смысл начать работу с маленького компонента типа Button и постепенно двигаться «вверх» по иерархии.
Извлечение компонентов разбиение компонентов на части повторное использование повторение частей интерфейса Посмотрите, на какие компоненты можно разбить компонент Comment ? function Comment(props) { return (
/>
{props.author.name}
{props.text}
{formatDate(props.date)}
); } Этот компонент представляет собой комментарий в социальной сети. Его пропсы включают в себя author (объект), text (строка), и date (дата). С этим компонентом может быть не очень удобно работать из-за излишней вложенности. Мы также не можем повторно использовать его составные части. Извлечение компонентов может сначала показаться неблагодарной работой. Тем не менее, в больших приложениях очень полезно иметь палитру компонентов, которые можно многократно использовать. Если вы не уверены, извлекать компонент или нет, вот простое правило. Если какая-то часть интерфейса многократно в нём повторяется ( Button , Panel , Avatar ) или сама по себе достаточно сложная ( App , FeedStory , Comment ), имеет смысл её вынести в независимый компонент.
Тест пройти тест по компонентам и состояниям
Практическая работа Практическая работа направлена на закрепление навыков создания React-компонентов функциональным и классовым способами, приобретения опыта работы с свойствами (props, пропсы) и состоянием (state) компонентов Упражнение 1 Описание React-компонента через функцию 1. Вместо константы book , создайте функциональный React-компонент Book() . Примечание: название компонента всегда записывается с заглавной буквы, а возвращается JSX: function Book(props) { const price = price ? price : ; return
{props.title}
Автор: {props.author} Цена: {price} руб.
; } 2. В метод ReactDOM.render() первым аргументом подставьте созданный функциональный React-компонент: 3. Убедитесь, что вывод информации остаётся прежним 4. Обратите внимание как аргументы из Book передаются в props ! Упражнение 2 Компонент App и композиция 1. Создайте функциональный React-компонент App и поместите его первым аргументом в render(): ReactDOM.render( , document.getElementById('root') ); 2. App должен находится в отдельном файле App.js и выводить информацию обо всех текущих книгах в dataBook , например так: function App(){ return
} 3. Примечание: вместо простого перечисления компонентов Book по количеству данных в dataBook , воспользуйтесь методом массивов map , чтобы вывести всё содержимое dataBook Упражнение 3 Конвертация функционального компонента в класс 1. Создайте файл Book.js для компонента Book
2. Создайте ES6 класс, расширяющий React.Component . У класса должно быть имя Book 3. Добавьте классу метод render() 4. Перенесите тело функции Book в метод render() 5. Замените props на this.props в теле render() 6. Удалите пустое описание функции 7. Убедитесь, что приложение работоспособно, исправьте ошибки 8. * Перепишите компонент App на класс компонент App
Состояние (state) state - объектное свойство , описывающее состояние компонента class Clock extends React.Component { constructor(props) { super(props); this.state = {date: new Date()}; } render() { } } «Состояние» очень похоже на уже знакомые нам пропсы, отличие в том, что состояние контролируется и доступно только конкретному компоненту.
Правильное использование состояний не изменяйте состояние напрямую (используйте setState() ) обновления могут быть асинхронными обновления состояния объединяются // Неправильно this.state.comment = 'Привет'; // Правильно this.setState({comment: 'Привет'}); Конструктор — это единственное место, где вы можете присвоить значение this.state напрямую. React может сгруппировать несколько вызовов setState() в одно обновление для улучшения производительности. Поскольку this.props и this.state могут обновляться асинхронно, вы не должны полагаться на их текущее значение для вычисления следующего состояния.
Методы жизненного цикла render componentDidMount componentWillUnmount componentDidUpdate Методы, которые вызываются на определённых стадиях работы библиотеки называются «методами жизненного цикла» (lifecycle methods) componentDidMount - запускается после того, как компонент отрендерился в DOM. В отличие от метода render() , componentDidMount() позволяет гибче использовать setState() . Вызов setState() здесь обновит состояние и вызовет другой рендеринг, но это произойдет до того, как браузер обновит пользовательский интерфейс componentDidMount(){ console.log(document.getElementById('root').outerHTML); fetch('http://mysite.foo/datas.json') .then(function(response) { return response.json(); }) } componentWillUnmount - запускается перед демонтажом компонента, используется для действий по очистке таймеров, очистке кеша, вызову API. Внимание, не получится изменить состояние в этом методе жизненного цикла componentWillUnmount (){ console.log('componentWillUnmount '); } ReactDOM.render( , document.querySelector('#root') ) setTimeout(()=>{ ReactDOM.render( , document.querySelector('#root') ) },4000); Метод можно использовать для отмены событий: componentWillUnmount() { window.removeEventListener('click', this.clickListener) } componentDidUpdate - вызывается, как только происходит обновление. Наиболее распространенный вариант использования метода componentDidUpdate() - это обновление DOM в ответ на изменения пропа или состояния. Также может использован для выполнения AJAX/fetch-запроса componentDidMount(){ console.log(document.getElementById('root').outerHTML); fetch('http://mysite.foo/datas.json') .then(function(response) { return response.json(); }) }
Практическая работа Работа с состояними и методами жизненного цикла Упражнение 1 Добавление локального состояния 1. Добавьте классу Book конструктор: единственным аргументом которого будет props . Примечание: вызовите родительский конструктор super(props) : constructor(props) { super(props); } 2. Добавьте компоненту Book начальное состояние selected , обозначающее логическую величину – выбрана книга или нет: this.state = {selected: false} 3. Перепишите render() так, чтобы в
по умолчанию выводился ещё один класс: book-default – если selected является false book-selected – если является true Примечание: для проверки работы подсмотрите DOM-структуру документа и/или добавьте CSS-свойства с селекторами указанных классов 4. Убедитесь, что если поменять this.state.selected на false , элемент приобретёт соответствующие CSS-классы book и book- selected 5. Добавьте в компонент Book две гиперссылки «Сравнить» и «В корзину». Примечание: href у ссылок будет содержать «#» Упражнение 2 Добавление методов жизненного цикла 1. Добавьте классу Book методы жизненного цикла: componentDidMount() { console.log('--','компонент смонтирован') } componentWillUnmount() { console.log('--','компонент будет демонтирован') } 2. Проверьте сообщение в консоли браузера Упражнение 3 Создание ключей (key) 1. При выводе компонентов, пропишите у каждого уникальный атрибут/проп key
Обработка событий onClick onMouseOver onChange
Обработка событий в React-элементах очень похожа на обработку событий в DOM-элементах. Но есть несколько синтаксических отличий: События в React именуются в стиле camelCase вместо нижнего регистра С JSX вы передаёте функцию как обработчик события вместо строки
Отмена действия по умолчанию function ActionLink() { function handleClick(e) { e.preventDefault(); console.log('По ссылке кликнули.'); } return ( Нажми на меня ); } В приведённом выше коде e — это синтетическое событие. React определяет синтетические события в соответствии со спецификацией W3C, поэтому всё кроссбраузерно. При использовании React обычно мы не вызываем addEventListener , чтобы добавить обработчики в DOM-элемент после его создания. Вместо этого добавляем обработчик сразу после того, как элемент отрендерился.
События в компонентах-классах обработчик события - обычно метод класса методы класса не привязаны к контексту стрелочные функции обратного вызова (callback ) передача аргументов В компоненте, определённом с помощью ES6-класса, в качестве обработчика события обычно выступает один из методов класса. Например, этот компонент Toggle рендерит кнопку, которая позволяет пользователю переключать состояния между «Включено» и «Выключено»: class Toggle extends React.Component { constructor(props) { super(props); this.state = {isToggleOn: true}; // Эта привязка обязательна для работы `this` в колбэке. this.handleClick = this.handleClick.bind(this); } handleClick() { this.setState(state => ({ isToggleOn: !state.isToggleOn })); } render() { return (
); } } ReactDOM.render( , document.getElementById('root') ); При обращении к this в JSX-колбэках необходимо учитывать, что методы класса в JavaScript по умолчанию не привязаны к контексту. Если вы забудете привязать метод this.handleClick и передать его в onClick , значение this будет undefined в момент вызова функции. Альтернативой может стать стрелочных функций в колбэке class LoggingButton extends React.Component { handleClick() { console.log('значение this:', this); } render() { // Такой синтаксис гарантирует, что `this` привязан к handleClick. return (
); } } Для передачи дополнительных аргументов в обработчик события можно воспользоваться следующим кодом
Две строки выше — эквивалентны, и используют стрелочные функции и Function.prototype.bind соответственно. В обоих случаях аргумент e , представляющий событие React, будет передан как второй аргумент после идентификатора. Используя стрелочную функцию, необходимо передавать аргумент явно, но с bind любые последующие аргументы передаются автоматически.
Практическая работа Обработка событий Упражнение 1 Назначение обработчика события 1. Опишите в гиперссылке «Сравнить» обработчик события: onClick={this.handleClick} 2. В классе создайте обработчик handleClick(e) , где аргумент e - объект-событие. Примечание: убедитесь в правильности указания контекста 3. Отмените действие по умолчанию e.preventDefault() 4. Проверьте работу по нажатию на ссылку. Примечание: можно вызывать alert() или console.log() 5. Установите состояние через this.setState() в handleClick() 6. Убедитесь, что при нажатии на «Сравнить» происходит выделение элемента за счет изменения состояния 7. Если событие не срабатывает, проверьте правильно ли указывается this : this.handleClick = this.handleClick.bind(this);//в конструкторе 8. Сделайте так, чтобы при повторном нажатии на «Сравнить», выделение снималось
Условный рендеринг показ компонентов в зависимости от состояния тернарный оператор оператор if переменные-элементы function UserGreeting(props) { return
С возвращением!
; } function GuestGreeting(props) { return
Войдите, пожалуйста.
; } React позволяет разделить логику на независимые компоненты. Эти компоненты можно показывать или прятать в зависимости от текущего состояния. Условный рендеринг в React работает так же, как условные выражения работают в JavaScript. Бывает нужно объяснить React, как состояние влияет на то, какие компоненты требуется скрыть, а какие — отрендерить, и как именно. В таких ситуациях используйте тернарный оператор JavaScript или выражения подобные if
Пример условной отрисовки function Greeting(props) { const isLoggedIn = props.isLoggedIn; if (isLoggedIn) { return ; } return ; } ReactDOM.render( // Попробуйте заменить на isLoggedIn={true} // и посмотрите на эффект. , document.getElementById('root') ); Компонент Greeting , который отражает один из этих компонентов в зависимости от того, на сайте пользователь или нет
Дополнения React-элементы можно хранить в переменных let button = JSX в логическом выражении {unreadMessages.length > 0 &&
У вас {unreadMessages.length} непрочитанных сообщений.
} предотвратить рендеринг можно возвращением null return null; JSX в логическом выражении Можно использовать особенность логических выражений в JavaScript, когда результатом становится не логическая величина Посмотрим на следующий пример. Если количество непрочитанных сообщений unreadMessages.length больше нуля, то есть условие сравнения даёт true , то результатом всего выражения становится JSX с h1 function Mailbox(props) { const unreadMessages = props.unreadMessages; return (
Здравствуйте!
{unreadMessages.length > 0 &&
У вас {unreadMessages.length} непрочитанных сообщений.
}
); } const messages = ['React', 'Re: React', 'Re:Re: React']; ReactDOM.render( , document.getElementById('root') ); Предотвращение отрисовки компонента В некоторых случаях компоненту нужно не показывать себя, для этого нужно вернуть null вместо JSX содержимого компонента. Например, будет ли содержимое WarningBanner отрендерено, зависит от значения пропа под именем warn . Если значение false , компонент ничего не рендерит: function WarningBanner(props) { if (!props.warn) { return null; } return (
Предупреждение!
); } Метод componentDidUpdate при этом всё равно будет вызван.
Практическая работа Условная отрисовка Упражнение 1 Назначение обработчика события 1. В классе App , в методе render() , создайте константу books и при помощи map() поместите все данные из dataBook в books 2. В операторе return вместо нескольких компонентов Book укажите books как выражение в JSX 3. В файле BookWithoutPrice.js создайте компонент BookWithoutPrice , который не будет отображать ссылки, если цена у книги указана как null 4. Измените код, который формирует вывод книг так, чтобы при отсутствии цены возвращался компонент BookWithoutPrice 5. Убедитесь, что книга с price равным null , не содержит ссылок «Сравнить» и «В корзину»
Формы эл-ты форм уже имеют состояние управляемые компоненты
При отправке формы, браузер переходит на новую страницу или заново открывает текущую. В React можно обрабатывать форму с помощью JavaScript В HTML элементы формы, такие как ,
); } В атрибуте value для поля ввода будет отображаться значение this.state.value . Состояние React-компонента стало «источником истины». А так как каждое нажатие клавиши вызывает handleChange , который обновляет состояние React-компонента, значение в поле будет обновляться по мере того, как пользователь печатает. В управляемом компоненте с каждыс изменением (мутацией) состояния связана функция-обработчик. Благодаря этому валидация или изменение введённого значения становится простой задачей
Отдельные элементы форм
Практическая работа Работа с элементами форм в React Упражнение 1 Создание формы добавления книги в каталог 1. Создайте новый компонент AddBookForm :
2. В конструкторе AddBookForm вызовите родительский конструктор и передайте в него props 3. Укажите состояние из полей одноимённых названию полей формы: this.state = { id: 0, title: '', author: '', price: '' } 4. укажите правильную привязку this в методах handleSubmit() и handleChange() . Примечание: методы будут созданы в этом упражнении позже 5. Встройте вывод AddBookForm перед списком всех книг 6. Укажите обработчик отправки формы handleSubmit() и обработчик изменения каждого поля handleChange() 7. Реализуйте в AddBookForm метод handleSubmit() . Метод должен проверять данные состояния и добавлять новую книгу. Примечание: отмените действие по умолчанию 8. Проверьте через консоль, что handleSubmit() действительно добавляет новую книгу. Например, isValidBook(book){ console.log(book) //только для проверки if (book.id && book.title && book.author ) return true; return false; } handleSubmit (ev) { ev.preventDefault() if( this.isValidBook(this.state) ){ dataBook.push(this.state); console.log(dataBook); //только для проверки } else alert("Заполните поля корректно") } 9. Опишите handleChange() так, чтобы поля заполнялись через значения состояния AddBookForm , а сам метод менял состояние через this.setState() : handleChange (ev, field) { ev.preventDefault() const input = {} input[ev.target.name] = ev.target.value this.setState(input) console.log(input) } 10. Убедитесь, что происходит корректное добавление новых книг 11. При возникновении ошибок, проверьте свой код и исправьте ошибки
render (){ return }
Подъём состояния Часто несколько компонентов должны отражать одни и те же изменяющиеся данные. Рекомендуется поднимать общее состояние до ближайшего общего предка В React совместное использование состояния достигается перемещением его до ближайшего предка компонентов, которым оно требуется. Это называется «подъём состояния». Для любых изменяемых данных в React-приложении должен быть один «источник истины». Обычно состояние сначала добавляется к компоненту, которому оно требуется для рендера. Затем, если другие компоненты также нуждаются в нём, вы можете поднять его до ближайшего общего предка. Вместо того, чтобы пытаться синхронизировать состояние между различными компонентами, вы должны полагаться на однонаправленный поток данных. Когда вы видите, что в UI что-то отображается неправильно, то можете воспользоваться расширением React Developer Tools . С помощью него можно проверить пропсы и перемещаться по дереву компонентов вверх до тех пор, пока не найдёте тот компонент, который отвечает за обновление состояния. Это позволяет отследить источник багов:
Практическая работа Организуйте всплытие состояния в приложении, которое разрабатываем на лабораторных работах Упражнение 1 Организация всплытия состояния - добавление товара в корзину 1. Создайте в компоненте App состояние items 2. В App cоздайте метод addBasket(), для добавления книги в items 3. В конструкторе App укажите корректную привязку привязку this : constructor(props) { super(props); this.state = {items: {}} this.addBasket = this.addBasket.bind(this); } 4. Заполните тело метода addBasket() : addBasket(id){ let items = Object.assign({},this.state.items); items[id] = (id in items) ? items[id]+1 : 1; console.log(items); this.setState({ items: items}); } 5. В методе render() компонента App укажите свойство (props) handleAddBasket() значение которого будет {this.addBasket} : render(){ const books = dataBook.map(book => { if(book.price) return /> else return /> } ) return
{books}
} Примечание: мы передаём через props-функцию, которую будем вызывать при добавлении товара в items при нажатии на «В корзину» в React-компоненте Book 6. В компоненте Book в render() добавьте обработчик нажатия на ссылку «В корзину»: В корзину 7. Опишите обработчик addBasketBook() . Примечание: отменить действие по умолчанию 8. Получите из props функцию handleAddBasket() и вызовите её с текущим номером книги: addBasketBook(e){ e.preventDefault(); const handleAddBasket = this.props.handleAddBasket; handleAddBasket(this.props.id) } 9. Убедитесь, что state.items в App пополняется новыми элементами Упражнение 2
Добавление компонента Basket 1. Создайте React-компонент класс Basket : class Basket extends React.Component { constructor(props) { super(props); this.deleteBasketItem = this.deleteBasketItem.bind(this); } deleteBasketItem(e){ e.preventDefault() const handleRemoveBasket = this.props.handleRemoveBasket const i = e.target.id //if(confirm("Точно хотите удалить?")) handleRemoveBasket(i) } getIndexById(id){ for(let p in dataBook) if (dataBook[p]['id'] == id) return p } render (){ let items = [], j, sum = 0; for(let i in this.props.items){ //console.log(i + " = " + this.props.items[i] + " (" + this.getIndexById(i) + ") ") j = this.getIndexById(i) sum += this.props.items[i] * dataBook[j]['price'] items.push(
class App extends React.Component { constructor(props) { super(props); this.state = {items: []} this.addBasket = this.addBasket.bind(this); this.removeBasket = this.removeBasket.bind(this); } addBasket (id){ let items = this.state.items.slice(0); if( id in items ) items[id]++ else items[id]=1 this.setState({items: items}) } removeBasket (id){ let items = this.state.items.slice(0), result = [] for(let i in items ){ if(i != id) result[i] = items[i] } this.setState({items: result}) } render(){ const books = dataBook.map(book => { if(book.price) return /> else return /> } ) return
{books}
} } 3. * Создайте компонент BasketItem и измените соответствующую часть метода render() класса Basket Упражнение 3 (при наличии времени) Создайте форму поиска по книгам 1. Добавьте и реализуйте компонент SearchForm для поиска книг по названию иавтору (через одно поле поиска)
Что далее вы завершили курс по React! но вы не завершили обучение 😉 пожалуйста, оставьте отзыв! квест 💡 Полезное create-react-app Redux, react-router... следующий курс create-react-app - удобная заготовка для быстрого старта по работе с React. Для начала работы нужно под рукой иметь node и выполнить команды инициализации и запуска проекта: npx create-react-app my-app cd my-app npm start Redux — библиотека для управления состоянием приложения. Может использоваться в любых приложениях на JavaScript, но чаще запускается с React React Router — библиотека для маршрутизации одностраничных приложений - сопоставляет URL вывод определённых компонентов React