Подключаем React и пробуем чего-нибудь сверстать на нем
React
Добавить еще пять секций. Для объявления секций использовать классы с функцией render. У каждой секции должен быть свой scss файлик.
Секции должны показывать своего рода обратный отчет. То есть на каждой пишем цифру, на последней пишем пуск. Типа:
- 5
- 4
- 3
- 2
- 1
- Пуск
Цифру центрировать по вертикали и горизонтали. Цвет фон прописывать в scss файлах, можно поиграться с другими стилями, например, с высотой, а также размером и цветом шрифта.
Что-то в таком роде сделать:

- Починить отслеживание последнего слайда, ну чтобы он не на 6 реагировал как сейчас, а на 9 или какой-то там по номеру последний
- Добавить хуки под слайд с практикой и слайд с кем можно стать
- Пробросить эти хуки внутрь SideMenu, по аналогии с isTopSlide и и isLastSlide
- Починить динамические стили, чтобы они учитывали старницу практики и страницу кем можно стать
Как делать задание 1
Ну я надеюсь у тебя nodejs уже установлен. Если вдруг нет, то скачай отсюда https://nodejs.org/en/ и установи.
Теперь создадим пустую папку и откроем ее с помощью Visual Studio Code

далее открываем консольку

и проверяем сначала что nodejs нормально установился. Пишем
node --version
и видим что-то такое:

если это есть значит все ок.
Считаю, что у тебя все ок =)
Создание проекта на react
Пишем в этой же консольке
npx create-react-app .
и ждем

я назвал свой папку с большими буквами, а так делать нельзя поэтому папку придется переименовать. Например в react_css_text

и открыть ее через visual studio code по новой

в общем, снова в консольке пишу
npx create-react-app .
ждем, пока будут скачаны все пакеты необходимые для работы react приложения, как и в случае со всяким современным javascript приложением качается пол интернета…

в общем, когда все закончится то увидим, что в папке появилась тьма файлов

плюс нам предлагают ввести команду npm start для запуска приложения (это примерно, как jekyll serve)
Пишим

и в браузере открывается

собственно, приложение на react запущено =О
Глянем что тут происходит. Самый главный файл это App.js, он представляет собой смесь javascript и html. Такой формат именуется JSX (java script extended). Это, по сути, главный шаблон приложения

Сразу встает вопрос если это базовый шаблон, то где тогда всякие <html><body> и т.д.
Ну на самом деле я немного наврал. На самом деле, тут два других файла являются самыми главными.
Первый – это public/index.html

который выглядит вот так если убрать комменты
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<meta
name="description"
content="Web site created using create-react-app"
/>
<link rel="apple-touch-icon" href="%PUBLIC_URL%/logo192.png" />
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
то есть обычный такой html. Например, если ты захочешь подключить fontawesome просто добавишь ссылку в head, в общем как обычно.
Внутри body нас интересует <div id="root"></div> это тег к которому будет привязываться react приложение. То есть именно в него будут добавляться все теги которые прописаны в App.js
Где ж собственно происходит привязка? А привязка происходит во втором главном файле index.html, находится в нем примерно следующее

Подключаем bootstrap
Вообще есть несколько способов подключения bootstrap к приложению. Я не знаю какой юзает Влад, потому покажу оба.
Способ №1
Самый простой. Идем на сайт bootstrap и вставляем

и

в public/index.html

теперь попробуем загнать бутстраповскую навигацию. Идем в App.js, сначала все убираем
import logo from './logo.svg';
import './App.css';
function App() {
return (
// убрал тут все
);
}
export default App;
Кстати если сохранить и попробовать глянуть что происходит в браузере, там увидим

Это потому что мы делаем return пустоты, а React требует тегов. Дадим их ему
Ищем в бутстрапе навигацию, какую-нибудь попроще: https://getbootstrap.com/docs/5.0/components/navbar/#nav
и копипастим, как-то так:
import logo from './logo.svg';
import './App.css';
function App() {
return (
<nav class="navbar navbar-expand-lg navbar-light bg-light">
<div class="container-fluid">
<a class="navbar-brand" href="#">Navbar</a>
<button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span class="navbar-toggler-icon"></span>
</button>
<div class="collapse navbar-collapse" id="navbarNav">
<ul class="navbar-nav">
<li class="nav-item">
<a class="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Features</a>
</li>
<li class="nav-item">
<a class="nav-link" href="#">Pricing</a>
</li>
<li class="nav-item">
<a class="nav-link disabled" href="#" tabindex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
</div>
</div>
</nav>
);
}
export default App;
Вообще, оно даже работает:

но это только на первый взгляд, если открыть консольку, там увидим красные варнинги

Первая ошибка требует переименовать все class в className, вторая жалуется на tabindex который надо переименовать в tabIndex
В общем конечно лениво, но выделяем слово class и тычем Ctrl+D, чтобы выбрать все повторения и переписываем на className, ну tabIndex внизу правим
Получится так:
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">Navbar</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<a className="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Features</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Pricing</a>
</li>
<li className="nav-item">
<a className="nav-link disabled" href="#" tabIndex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
</div>
</div>
</nav>
);
ошибки ушли

ну точнее остались желтые ошибки, но они носят рекомендательный характер. В данном случае он ругается что у ссылок href-ы ведут в никуда… Ну и пусть.
Теперь настроим visual studio, чтобы она начала нормально поддерживать вот эти className вместо class. Ну типа, когда пишешь точка + имя класса, то вставляется div с указанным классом.
Мне это важно, так как часто пользуюсь. Идем в настройки

тыкаем там

и вводим там javascript и javascriptreact

попробуем теперь что-нибудь написать

красота =)
Способ №2
Это более прогрессивный способ. Заключается в установки пакета bootstrap.
Сначала идем в public/index.html и удаляем там подключение через теги, остается просто

теперь открываем консольку и пишем
npm install -S bootstrap

и ждем:

теперь идем в App.js и подключаем bootstrap через импорт:
import logo from './logo.svg';
import './App.css';
require('bootstrap'); // подключил скрипты
require('bootstrap/dist/css/bootstrap.css'); // подключил стили
function App() {
return (
// ...
смотрим:

ошибка =О
Бутстрапу надо еще один пакет установить, который называется @popperjs/core
Снова идем в консольку и там пишем:
npm install -S @popperjs/core
и снова ждем:

как закончит, надо добавить в App.js где-нибудь пробел, чтобы react понял, что файл изменился и можно проверять:

вот теперь другое дело.
Чем второй способ лучше. Ну он более каноничный при разработка js-приложения, плюс у тебя используется локальная копия bootstrap, а значит можно верстать даже без интернета.
В принципе в этом состоянии уже можно верстать одностраничники прям в один файл, правда это не канонично >__>
Как делать задание 2
Продолжим с нашим кодом из прошлого задания.
import './App.css';
require('bootstrap');
require('bootstrap/dist/css/bootstrap.css');
function App() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">Navbar</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<a className="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Features</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Pricing</a>
</li>
<li className="nav-item">
<a className="nav-link disabled" href="#" tabIndex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
</div>
</div>
<div className="my-class-name"></div>
</nav>
);
}
export default App;
Стилизация
Если тебе надо создавать новые классы или переопределять существующие. То тут все просто. Взглянем на файлик App.css, по идее он должен содержать стили специфичные для html в App.js.
Почистим его

тут можно писать любой валидный css без всяких ограничений. Например, хочу я чтобы активные ссылки были с желтым фоном, пишу в него
.nav-link.active {
background-color: yellow;
}
и сразу видим результат:

Кстати если нам вдруг захочется использовать scss. То надо сделать следующее.
Открываем консольку и пишем там:
npm install -S sass
ждем

Теперь переименовываем App.css в App.scss и правим импорт:
import './App.scss'; // заменил на App.scss
require('bootstrap');
require('bootstrap/dist/css/bootstrap.css');
function App() {
function App() {
return (
// ...
еще мне пришлось перезапустить npm, для этого я в консольке где я писал npm run start нажал Ctrl+C, затем, когда меня спросили точно ли я хочу прервать приложение, я нажал Y, а потом снова написал npm start
Работает так же

но уже с использование scss.
Разбиение верстки
Допустим я хочу, чтобы у меня был стандартная страница с меню навигации, контентом и футером. Я могу все пилить прямо в App.js, добавить после навигации два div`а. И встретить первую проблему:

дело в том, что в react, любая компонента (а App это компонента) может возвращать только тег у которого нет родителей. А у нас тут таких целых три.
В этом случае, надо просто обернуть все содержимое в специальный тег <React.Fragment>, вот так:

тут правда надо еще вверх добавить import этого самого React
import './App.scss';
import React from "react"; // добавил
require('bootstrap');
require('bootstrap/dist/css/bootstrap.css');
function App() {
return (
<React.Fragment>
<!-- ... -->
от теперь работает:

и вроде все прекрасно, только когда контент становится сильно большим, поддерживать такую кучу верстки становится не удобно. У нас сейчас даже та же навигация занимает тьму места.
Поэтому в react очень легко можно бить верстку на компоненты/секции и т.п.
Давай добавим компоненту под навигацию, создаем файлик Navigation.js

и пишем в него
function Navigation() {
return (
<nav className="navbar navbar-expand-lg navbar-light bg-light">
<div className="container-fluid">
<a className="navbar-brand" href="#">Navbar</a>
<button className="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarNav" aria-controls="navbarNav" aria-expanded="false" aria-label="Toggle navigation">
<span className="navbar-toggler-icon"></span>
</button>
<div className="collapse navbar-collapse" id="navbarNav">
<ul className="navbar-nav">
<li className="nav-item">
<a className="nav-link active" aria-current="page" href="#">Home</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Features</a>
</li>
<li className="nav-item">
<a className="nav-link" href="#">Pricing</a>
</li>
<li className="nav-item">
<a className="nav-link disabled" href="#" tabIndex="-1" aria-disabled="true">Disabled</a>
</li>
</ul>
</div>
</div>
</nav>
);
}
export default Navigation; // обязательно эту строчку добавляем
то есть своего рода объявили функцию которая возвращает разметку.
Теперь идем в App.js и удаляем оттуда навигацию:
import './App.scss';
import React from "react";
require('bootstrap');
require('bootstrap/dist/css/bootstrap.css');
function App() {
return (
<React.Fragment>
// тут была навигация, теперь нет
<div className="content container">
Очень-очень интересный контент
</div>
<div className="footer container">
Футер
</div>
</React.Fragment>
);
}
export default App;
и теперь самое интересное.
Вверху в App.js добавляем импорт компоненты Navigation, которую мы только что собственноручно сделали:
import './App.scss';
import React from "react";
import Navigation from './Navigation'; // добавил импорт навигации
require('bootstrap');
require('bootstrap/dist/css/bootstrap.css');
function App() {
return (
и используем эту компоненту как новый тег:
function App() {
return (
<React.Fragment>
<Navigation /> // добавил навигацию
<div className="content container">
Очень-очень интересный контент
</div>
<div className="footer container">
Футер
</div>
</React.Fragment>
);
}
и все должно отрендерится прямо как раньше:

Теперь попробуй сама вынести остальные компоненты =О
Как делать задание 3
Никто не мешает делать вложенность по компонентам хоть до бесконечности.
Например, есть у нас компонента Content
function Content() {
return (
<div className="content container">
Очень-очень интересный контент
</div>
)
}
export default Content;
мы можем внутри контента добавить компоненты под секции. Но чтобы папка src не забивалась у нас мусором. Мы создадим в ней подпапку и будем создавать компоненты уже внутри нее

кстати, Влад используем немного другой формат объявления компонент. Он немного более сложный, но позволяет более гибко управлять поведением. Поведение нам пока не очень интересно. Так что просто рассмотрим, как объявлять компоненты таким образом.
В общем пишем в Section1.js:
import React from 'react'; // импортнули реакт
class Section1 extends React.Component { // объявили класс под секцию
render() { // переопределил метод render
return (
// тут обычная верстка идет
<section className="section1 bg-primary p-3 text-white">
Первая секция
</section>
);
}
}
export default Section1;
ну а дальше запихиваем как обычно:
import Section1 from "./content/Section1"; // импортнули секцию
function Content() {
return (
<div className="content container">
Очень-очень интересный контент
<Section1 /> // подключили как новый тег
</div>
)
}
export default Content;
если мы, например, хотим стилизовать секцию, мы можем конечно писать все в App.scss, но более грамотно создать отдельный файлик под Section1 и прописать стили туда:

а потом подключить его в Section1.js
import React from 'react';
import"./Section1.scss"; // подключил
class Section1 extends React.Component {
render() {
// ...
}
}
export default Section1;
проверяем:

Уря!!!11111 =)
Как делать задание 4
Тут попробую показать общую идею стейтов в реакте.
Открываем файлик Page.jsx и обращаем внимание на вот эти две строчки

это так называемые объявление переменных состояния. Когда мы вызываем функцию React.useState происходит создание такое переменной. В скобочках указывается ее начальное значение. В качестве результата работы этой функции у нас на самом деле приходит переменная и функция, с помощью которой мы можем менять ее значение.
Т.е., например, isTopSlide – это переменная, а setIsTopSlide – это функция с помощью которой можно изменить значение переменной.
Зачем все так сложно? Отчасти в целях оптимизации кода, но в основном конечно просто потому что это реакт.
Попробуем понять нафига нам создавать переменные через React.useState.
Допустим я хочу добавить кнопочку, на которой будет нарисована циферка, и я тыкаю на кнопку а значение этой цифры будет увеличиваться.
Добавим сначала кнопку, вот такую
<button className="position-fixed btn btn-primary m-2" style={{zIndex: 10000, bottom: 0, right: 0}}>
0
</button>
куда-нибудь сюда:

получится так

сейчас у меня на ней выводится 0.
Я могу сделать чтобы на ней выводилось значение какой-нибудь переменной, объявляем ее и выводим:

проверяем

значение заработало.
Теперь я хочу сделать так чтобы кликая на кнопку происходило увеличение этой переменной на 1, ну и соответственно было бы неплохо, чтобы реакт эти изменения подхватывал. Так как реакт – реактивный фреймворк, было бы логично чтобы так оно и произошло.
В общем добавляем функцию. Примерно, как мы уже делали на чистом js, только привязку делаем не через onclick а через onClick и фигурные скобки

пробуем теперь тыкать:

чет не тыкается, тут варианта два либо мы неправильно привязали, и функция не сработала. Либо мы что-то делаем не так.
Проверить что функция работает, достаточно легко, просто добавим console.log в нее
function increaseMyVariable() {
myVariable += 1;
console.log(myVariable);
}
и глянем что будет выводиться:

так, ну по крайне мере мы знаем, что функция срабатывает. Так почему же реакт не подхватывает изменения? А ПОТОМУ ЧТО ОН ТУПОЙ!!! =О
Ой извините, в общем, он не очень интеллектуальный. Короче, чтобы он понял, что значение переменной обновилась ему надо об этом сообщить. Для этих целей реакт предлагает нам создавать переменные не напрямую, а через использование React.useState (вообще, к слову, эта фиговина называется хуком, https://ru.reactjs.org/docs/hooks-intro.html, по заверениям разработчиков, современный программист не умеет в классы, а работать через хуки очень весело и прогрессивно, но мы то знаем…)
И так, согласно документации создаем этот самый хук, то есть объявляем переменную вот так
// let myVariable = 123; ЭТО УБИРАЕМ
const [myVariable, setMyVariable] = React.useState(123); // а это подставляем
теперь наша функция
function increaseMyVariable() {
myVariable += 1;
console.log(myVariable);
}
становится не корректной, потому что мы не можем менять значения myVariable, потому что объявили ее через const. Теперь все изменения значений надо делать через setMyVariable.
Правим функцию:
function increaseMyVariable() {
// myVariable += 1; УБИРАЕМ
setMyVariable(myVariable + 1); // меняем значение myVariable на myVariable + 1, то есть увеличиваем ее на 1
console.log(myVariable);
}
попробуем потыкать:

о, теперь начал реагировать =О
Проброс значения переменной в другую компоненту
Ну ладно, уберем теперь кнопочку и все что с ней связано. И посмотрим, что у нас есть в этом файлике Page.

Есть две переменные isTopSlide, isLastSlide, одна отслеживает, когда у нас активен первый слайд, вторая отслеживает, когда активен последний слайд.
Есть некая функцию onLeave, она привязана к библиотечки fullPage.js и вызывается каждый раз когда изменяется слайд. Не будем вникать как я ее написал, просто разберем ее параметры. Нас интересует ровно два

эти origin и destination объекты у которых есть свойство index, в которых указан номер слайда. 0 – для первого слайда, и 5 – для шестого слайда, когда слайдов было меньше он был последним.
И получается, что в функции мы меняем значение переменных isTopSlide, isLastSlide в соответствии с активным слайдом.
То есть когда у нас будет открыт титульный слайд, то в destination.index окажется 0, и стало быть вызовется функция setIsTopSlide с параметром true (ведь 0 == 0), и значит в переменной isTopSlide окажется true.
Можно проверить как это работает. Добавляем в разметку
<div className="position-fixed m-2" style={{zIndex: 10000}}>{isTopSlide.toString()}</div>

и смотрим что выводится:

зачем нужны эти переменные? Я их добавлял, чтобы менять цвет гамбургера либо чтобы прятать его на последней странице.
Каким образом? Я пробрасываю их в компоненту ответственную за менюшку

если зайти в SideMenu мы увидим там те самые isTopSlide, isLastSlide

зачем оно так пишется? Идея в том что когда мы пишем в фигурных скобках {isTopSlide, isLastSlide}, мы по сути создаем для тега SideMenu два атрибута в которые можно передать значения. Собственно, значения которые мы прописываем в теге пойдут в эти переменные

мы всегда можем добавить новый атрибут для SideMenu, вот так:
function SideMenu({isTopSlide, isLastSlide, newAttribute}){
и передавать в него значение в разметке

то есть передавать не обязательно переменную, можно просто значение. Но переменную интереснее.
Я могу даже вывести значение этого атрибута

получится так:

а могу вывести туда значение isTopSlide

получится так:

То есть получается, что значение я меняю в Page, а результат использую в slide. Ето, без шуток, очень прогрессивно =О
Ну и вопрос, а каким образом я меняю цвет и прячу навигацию?
Для этого используется еще одна библиотечка react-spring. Точнее можно и без нее, но она добавляет эффект постепенного изменения значения. С помощью нее я создаю два динамических стиля
// отвечает за цвет гамбургера
const { fill } = useSpring({
fill: (isTopSlide || isLastSlide || isOpen) ? "#fafafa" : "#000000"
});
// отвечает за то чтобы прятать гамбургер
const menuContainerStyle = useSpring({
opacity: isLastSlide ? '0%' : "100%"
})
эти специальные переменных передаются в атрибуты тегов

в обоих случаях используется обыкновенный тернарный оператора. То есть для цвета
(isTopSlide || isLastSlide || isOpen) ? "#fafafa" : "#000000"
что означает
Если первый слайд, либо последний слайд, либо меню открыто, то используй цвет #fafafa иначе используй #000000
ну и для второго
isLastSlide ? '0%' : "100%"
если последний слайд, то установи объекту видимость 0%, иначе 100%
Так как у нас isLastSlide работает не правильно, и становится true когда на самом деле шестой слайд, то и работает у нас все криво.
За счет этого все и работает.
Надеюсь какие-то мысли появились. А раз так, то айда пилить задание! =О