Translate

Поиск по этому блогу

понедельник, 19 августа 2019 г.

React - шпаргалка.

Шпаргалка по React.js в картинках.


См. этот пост на gitHub                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer

четверг, 15 августа 2019 г.

Методы жизненного цикла React 16 на русском.


//  React 16 Lifecycle Methods

class ComponentWithLifecycle extends React.Component {
  //  Часто используемые методы ЖЦ (Жизненного цикла)
  constructor() {
    //  - ЖЦ: Mounting  - Монтаж ( до рендера )
    //  - Цель: Инициализация состояния
  }

  componentDidMount() {
    //  - ЖЦ: Mounting - Монтаж ( немедленно после рендера )
    //  -Цель: Инициализация состояний требующее существование ДОМ узлов
    //    Сетевые запросы и побочный эффекты.
  }
   componentDidUpDate() {
    //  - ЖЦ:  Updating - Обновление 
( yнемедленно после построения - обновления)
    //  -Цель: Управление обновлением ДОМ или поддержка сетевых запросов
   }
   componentWillUnmount() {
      //  - ЖЦ: Unmounting - Размонтирование
      // -Цуль: Убрать такие вещи как обработчики событий,
 отмена сетевых запросов и т.д.
   }

  //  Редко используемые методы жизненного цикла
  static static getDerivedStateFromProps(props, state) {
    //  -ЖЦ: Mounting  Монтирование и размонтирование -  Unmounting
( немедленно до рендера )
    //  -Цель: Когда state - состояние зависит от свойств - props 
( следует избегать )
  }

  shouldComponentUpdate(nextProps, nextState) {
    //  -ЖЦ: Updating -Обновление (немедленно до рендера )
    //  -Цель: Позволяет разработчику предотвращать рендеринг
  }

  getSnapshotBeforeUpdate(prevProps, prevState) {
    //  -ЖЦ: Updating - Обновление 
( непосредственно перед подтверждением вывода рендера )
    //  -Цель Захват информации ДОМ, такой как данные скролла - 
scroll position которые могут меняться.
  }

  // Особые случаи использования
  componentDidCatch() {
    //  - Создает границы ошибок для захвата их от дочерних компонентов
  }

  // Необходимый - обязательный
  render() {
    // -Выводит код компонента на экран
  }

  // Объяснение жизненного цикла:
  // Mounting -  монтаж: Вызывается до того как ваш компонент 
отображается в пользовательском интерфейсе - UI
  // Updating - обновление: Вызывается изменением  
state-состояния или props и повторный рендеринг UI
  // Unmounting - Размонтирование: Вызывается когда ваш 
компонент больше не будет отображаться в UI
}
React Lifecycle Methods - on GitHub                                                                                                                                                              

Телеграм канал - Full Stack JavaScript Developer

среда, 5 июня 2019 г.

React-Сайт ( IV ). Навигация по странице. Деплой приложения

Нам остается закончить навигацию, которую мы начали в первой части и выложить наше приложение в сеть.




Все статьи этого проекта:




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





Для того, чтобы оно заработало так как нам надо, и осуществлялись плавные переходы между секциями, мы воспользуемся готовым пакетом react-scroll. Если вы еще не установили этот пакет, то самое время это сделать:

npm install react-scroll


Все наше меню сейчас находится в файле src/Components/Header_footer/SideDrawer.js

import React from "react";
import Drawer from "@material-ui/core/Drawer";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
const SideDrawer = props => {
  return (
    <Drawer
      anchor="right"
      open={props.open}
      onClose={() => props.onClose(false)}
    >
      <List component="nav">
        <ListItem button onClick={() => console.log("Start Events")}>
          Event starts in
        </ListItem>

        <ListItem button onClick={() => console.log("React info")}>
          React info
        </ListItem>

        <ListItem button onClick={() => console.log(" Our Team")}>
          Our Team
        </ListItem>

        <ListItem button onClick={() => console.log("Pricing")}>
          Pricing
        </ListItem>

        <ListItem button onClick={() => console.log("Location")}>
          Location
        </ListItem>
      </List>
    </Drawer>
  );
};

export default SideDrawer;



Для того, чтобы оно полноценно заработало, нам нужно создать в компоненте функцию, которая будет получать нужный элемент для скрола (куда, точнее - к чему скролить) в качестве аргумента. Более подробно, все есть в документации к пакету по ссылке в начале текста.

import { scroller } from "react-scroll";
//......code components
    const scrollToElement = element => {
        scroller.scrollTo(element, {
        duration: 1500,
        delay: 100,
        smooth: true,
        offset: -80
        });
        props.onClose(false);
    };


Значение offset: -80 помогает передвигаться именно к началу элемента. Я его подобрал эмпирически.

Теперь вывод в консоль для каждого элемента мы заменим на эту функцию, куда и передадим нужный элемент в качестве аргумента.

src/Components/Header_footer/SideDrawer.js
import React from "react";
import { scroller } from "react-scroll";
import Drawer from "@material-ui/core/Drawer";
import List from "@material-ui/core/List";
import ListItem from "@material-ui/core/ListItem";
const SideDrawer = props => {
  const scrollToElement = element => {
    scroller.scrollTo(element, {
      duration: 1500,
      delay: 100,
      smooth: true,
      offset: -80
    });
    props.onClose(false);
  };

  return (
    <Drawer
      anchor="right"
      open={props.open}
      onClose={() => props.onClose(false)}
    >
      <List component="nav">
        <ListItem button onClick={() => scrollToElement("start-events")}>
          Event starts in
        </ListItem>

        <ListItem button onClick={() => scrollToElement("react-info")}>
          React info
        </ListItem>

        <ListItem button onClick={() => scrollToElement("out-team")}>
          Our Team
        </ListItem>

        <ListItem button onClick={() => scrollToElement("pricing")}>
          Pricing
        </ListItem>

        <ListItem button onClick={() => scrollToElement("location")}>
          Location
        </ListItem>
      </List>
    </Drawer>
  );
};

export default SideDrawer;



Пока что, ничего работать не будет.

Для этого нам нужно в компоненте App.js взять из этой библиотеки Element

import { Element } from "react-scroll";

И обернуть в него все компоненты.


import React from "react";
import "./resources/style.css";
import { Element } from "react-scroll";
import Header from "./Compomemts/Header_Footer/Header";
import Slider from "./Compomemts/Slider";
import Info from "./Compomemts/Info";
import Highlights from "./Compomemts/Highlights";
import Pricing from "./Compomemts/Pricing";
import Location from "./Compomemts/Location";
import Footer from "./Compomemts/Header_Footer/Footer";
function App() {
  return (
    <div className="App">
      <Header />
      <Element name="start-events">
        <Slider />
      </Element>
      <Element name="react-info">
        <Info />
      </Element>
      <Element name="out-team">
        <Highlights />
      </Element>
      <Element name="pricing">
        <Pricing />
      </Element>
      <Element name="location">
        <Location />
      </Element>

      <Footer />
    </div>
  );
}

export default App;


Обратите внимание, что имя элемента - name="value" (в данном случае - value) должно совпадать с тем значением, которое мы передали функции в файле SideDrawer.js

Таким образом мы получили полностью рабочее меню с плавным скролом между секциями.

Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Scroll menu"


Деплой

Мы пойдем очень простым путем. Для деплоя воспользуемся самым простым сервисом - Surge.

Если вы ранее не работали с ним, то там есть отличная документация и ее стоит прочитать. А вообще, все очень и очень просто.

Для начала нам нужно собрать наше приложение ( build ). Для этого запустим команду

npm run build


И подождем некоторое время, пока не закончится создание готовой для деплоя версии.

В корне приложения появится новая папка - build, в которой и будут находиться готовые для выкладывания в интернет файлы нашего приложения.

Пока идет работа по строительству файлов, мы установим необходимый пакет surge глобально:

npm install --global surge


Потребуется ввести адрес электронной почты и придумать пароль. Более подробно на сайте Getting started with Surge.

Когда закончится билдинг, то у вас в корне сайта появится папка build и именно ее мы разместим на сервере Surge.

Для этого нам достаточно ввести одну команду

surge -p build


В данном случае мы используем флаг -p, чтобы выложить не все файлы, а только те, что в папке build



После того, как приложение начнет загружаться, нужно нажать Enter, для того, чтобы согласиться на их домен.



После полной выгрузки приложения нам остается просто перейти по предоставленной ссылке, чтобы увидеть его в действии уже на сайте.

Готовый сайт на surge.sh



                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer

понедельник, 3 июня 2019 г.

React-Сайт ( III ). Цены-локация секции.

В этой части сделаем две очень важные секции - секция вывода цены и локации.




Все статьи этого проекта:




Задачу разделим на два этапа. На первом этапе создадим секцию цен. Это довольно простая задача, но есть одно но.



Если посмотреть на картинку, то все выглядит довольно просто. У нас есть три блока, которые находятся внутри секции. Каждый блок имеет свои значения цены, описания, позиции и главное, то чего вы не видите на статической картинке - нам нужно выводить блоки с последовательным проявлением. Вначале должен показаться блок. который по центру и после задержки в пол сек. уже первый и третий блоки.

Кнопки, мы уже создали отдельный компонент MyButton в который мы передадим нужные свойства. Это можно во внимание уже не брать.

Задачу можно решить разными путями. Я решил все это создать в одном компоненте. Данные можно передать в этот компонент извне, но так как мы можем их сохранять в самом компоненте. то это еще одно упрощение задачи. Данные можно сохранить в виде отдельных массивов (массив - цен, описаний, и пр.), тогда мы сможем их выводить в нужном порядке используя индекс в одном переборе (map()) и по индексу выводить значения из других массивов. Например так:

 const arr_1 = ['Price-1', 'Price-2', 'Price-3'];
 const arr_2 = ['Description-1', 'Description-2', 'Description-3'];
 const arr_3 = ['Something-1','Something-2','Something-3'];

 function iter(arr_1, arr_2, arr_3){
   arr_1.map((item, index)=>(console.log("Price = "+ arr_1[index] + "; Description = " + arr_2[index] + "; Something = " + arr_3[index] + "Index = " + "; Index = " + index)))
 }
 iter(arr_1, arr_2, arr_3);

// Price = Price-1; Description = Description-1; Something = Something-1Index = ; Index = 0
// Price = Price-2; Description = Description-2; Something = Something-2Index = ; Index = 1
// Price = Price-3; Description = Description-3; Something = Something-3Index = ; Index = 2




В данном случае способ вывода в консоль значения не имеет. Главное что омы получаем таким образом доступ к нужным данным, которые все равно, мы будем выводить в jsx.

Второй вариант - это (обычный) сделать один массив данным с отдельными объектами - в виде каждого блока. У каждого объекта будут ключи - поля (name, price, description, position, link, delay). Вы можете выбрать любой вариант,который вам больше нравится. Главное. что в любом случае у нас будет возможность задать нужную задержку выполнения появления блока - delay и применить ее к нужному блоку.

Способ хранения данных (внутри отдельного компонента или во внешнем файле) для нас сейчас значения не имеет, но стоит обратить внимание на то как вы храните эти значения. Я встречал сохранение статических данных в state компонента. Считаю это излишним, потому что в стейтах есть смысл хранить изменяемые значения, а не статические, как в данном случае. Это облегчит создание самих компонентов, ускорит их работу и избавит от проблем отладки в будущем. Хватит слов. К коду.

Создадим отдельную папку и индексовый файл для нашего компонента в папке компонентов. Как мы и договорились, это будет компонент без состояния. Сразу же подключим и выведем его в файле App.js

import Pricing from './Compomemts/Pricing';

<Pricing />

Для Components/Pricing/index.js
import React from "react";

const Pricing = () => {
  return (
     <div className="bck_black">
       <div className="center_wrapper pricing_section">
         <h2>Pricing</h2>

         <div className="pricing_wrapper">Some text</div>
       </div>
     </div>

);
};

export default Pricing;



Убедились, что все работает. Слева внизу появится в браузере слово - Pricing.

Теперь внутри нашего компонента создадим общую разметку для вывода трех блоков - выше в коде красным. Добавляем слили

recourses/style.css
/* ====================>>> PRICING <<<=================================== */


.pricing_section {
    padding: 70px 0px;
}


.pricing_section h2 {
    color: #ffffff;
    text-transform: uppercase;
    text-align: center;
    font-size: 50px;
    margin: 0;
}

.pricing_wrapper {
    display: flex;
    flex-wrap: wrap;
}
.pricing_wrapper .pricing_item{
    flex-grow: 1;
    width: 33%;
    padding: 20px;
    box-sizing: border-box;
}

.pricing_wrapper .pricing_inner_wrapper {
    border:2px solid #ffa800;
    padding: 50px 20px;
}

.pricing_inner_wrapper .pricing_title {
    color: #ffffff;
    text-align: center;
    border-bottom: 1px solid #ffa800;
    padding-bottom: 20px;
}
.pricing_inner_wrapper .pricing_title span:nth-child(1) {
    font-size: 50px;
    display: block;
}
.pricing_inner_wrapper .pricing_title span:nth-child(2) {
    text-transform: uppercase;
    font-size: 28px;
    font-weight: 300;
}
.pricing_inner_wrapper .pricing_description {
    color: #b8b8b8;
    font-weight: 300;
    font-size: 14px;
    text-align: center;
    padding: 20px 0px;
    min-height: 70px;
}

.pricing_inner_wrapper .pricing_buttons {
     text-align: center;
}



После этого в браузере мы увидим наше слово выеденное один раз.



Данные я решил собрать вместе в массив объектов и разместил их внутри компонента. Все данные произвольные, кроме data.delay, значения которым мы дали согласно тому в какой последовательности блоки будут выводиться на экран.

Components/Pricing/index.js
  const data = [
    {
      prices: 100,
      positions: "Balcony",
      desc:
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt uts",
      linkto: "https://twitter.com/JroslavK",
      delay: 500
    },
    {
      prices: 150,
      positions: "Medium",
      desc:
        "Dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea",
      linkto:
        "https://www.facebook.com/Yaroslav-Web-Master-1446556072148794/?modal=admin_todo_tour",
      delay: 0
    },
    {
      prices: 250,
      positions: "Star",
      desc:
        "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
      linkto:
        "https://www.facebook.com/groups/1786288918273718/?ref=bookmarks",
      delay: 500
    }
  ];


Импортируем в этот файл кнопку MyButton, которую мы создали в прошлом посте.

import MyButton from '../Utils/myButton';

Сразу же импортируем и Zoom эффект из react-reveal

import Zoom from "react-reveal/Zoom";

В этот компонент - Zoom мы будем оборачивать наши компоненты в цикле и передавать им задержку показа на странице - datadelay каждого компонента отдельно.

Там где у нас сейчас выводится слово Some thing, мы будем выводить функцию, которая будет перебирать наши данные (методом map) и вернет разметку в которую поместит соответствующие данные каждого блока.

Функцию привожу ниже. В ней нет ничего необычного. Все как всегда. перебираем данные и выводим в нужных местах разметки.

Components/Pricing/index.js
const showBoxes = () =≶
  data.map((item, i) =≶ (
    <Zoom delay={item.delay} key={i}≶
      <div className="pricing_item"≶
        <div className="pricing_inner_wrapper"≶
          <div className="pricing_title"≶
            <span≶${item.prices}</span≶
            <span≶{item.positions}</span≶
          </div≶
          <div className="pricing_description"≶{item.desc}</div≶
          <div className="pricing_buttons"≶
            <MyButton
              text="Purchase"
              bck="#ffa800"
              color="#ffffff"
              link={item.linkto}
            /≶
          </div≶
        </div≶
      </div≶
    </Zoom≶
  ));



Для того, чтобы все нормально отобразилось на странице, мы вместо вывода слова Some thing сделаем вызов нашей функции.

<div className="pricing_wrapper">{showBoxes()}</div>

Теперь как только компонент загрузится, вызовется функция, которая вернет нам готовую разметку для каждого из трех блоков, уже с соответствующими данными.

Components/Pricing/index.js полностью

import React from "react";
import Zoom from "react-reveal/Zoom";
import MyButton from "../Utils/myButton";

const Pricing = () => {
  const data = [
    {
      prices: 100,
      positions: "Balcony",
      desc:
        "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt uts",
      linkto: "https://twitter.com/JroslavK",
      delay: 500
    },
    {
      prices: 150,
      positions: "Medium",
      desc:
        "Dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea",
      linkto:
        "https://www.facebook.com/Yaroslav-Web-Master-1446556072148794/?modal=admin_todo_tour",
      delay: 0
    },
    {
      prices: 250,
      positions: "Star",
      desc:
        "Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
      linkto: "https://www.facebook.com/groups/1786288918273718/?ref=bookmarks",
      delay: 500
    }
  ];

  const showBoxes = () =>
    data.map((item, i) => (
      <Zoom delay={item.delay} key={i}>
        <div className="pricing_item">
          <div className="pricing_inner_wrapper">
            <div className="pricing_title">
              <span>${item.prices}</span>
              <span>{item.positions}</span>
            </div>
            <div className="pricing_description">{item.desc}</div>
            <div className="pricing_buttons">
              <MyButton
                text="Purchase"
                bck="#ffa800"
                color="#ffffff"
                link={item.linkto}
              />
            </div>
          </div>
        </div>
      </Zoom>
    ));
  return (
    <div className="bck_black">
      <div className="center_wrapper pricing_section">
        <h2>Pricing</h2>

        <div className="pricing_wrapper">{showBoxes()}</div>
      </div>
    </div>
  );
};

export default Pricing;



Если посмотреть в браузере, то можно увидеть, что получилось именно то, что и хотели.



Блоки выводятся в нужной последовательности.

Да.Прошу прощения. Я по невнимательности опустил опечатку в файле Itils/MyButton.js, там в свойствах кнопки, должно быть конечно size="small". После того, как все исправил, я убедился что ошибок в консоли нет.

Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Pricing section"


Локация

Цель - вывести секцию, которая займет 100% ширины и на которой будет показана гугл-карта с местонахождением события.

Здесь все проще, чем вы могли бы подумать. В папке компонентов для этого создадим отдельную папку Location с индексовым файлом - index.js и с обычным кодом для компонента без состояния:

src/Components/Location/index.js
import React from "react";

const Location = () => {
  return (
         <div >
          //   Здесь будет вся разметка
         </div>
      )
};

export default Location;


Добавим классы, для разметки.

Основной див у нас поучит className="location_wrapper", а внутри него создадим отдельный див, для вывода наименования секции - className="location_tag"

Сразу добавим стили:

resources/style.css

.location_wrapper {
    position:relative;
}

.location_tag {
    position: absolute;
    bottom: 0px;
    width: 100%;
}
.location_tag div{
    background: #2c2c2c;
    color: #ffffff;
    text-transform: uppercase;
    width: 220px;
    margin: 0 auto;
    font-size: 29px;
    padding: 15px 20px;
    text-align: center;
}


Далее идем в Гугл карты и находим нужный нам регион и объект. Наша задача получить код для фрейма, который мы и выведем в компоненте реакт.

Вводим адрес. В моем случае это будет Amsterdam, Gedempt Hamerkanaal 231, De Kromhouthal. Нажимаем поделится - Share



В другом окне выбираем "встроенная карта" - Embeded Map



Убеждаемся, что у нас то, что надо показано на малом фрейме и берем нужный код - любым способом его копируем.



Возвращаемся в наше приложение и вставляем то, что скопировали сразу под основным дивом.

Теперь немного его доработаем. например изменим allowfullscreen на allowFullScreen добавим ширину 100% и высоту 500px. Да и важно. Теперь нам нужно отдельно добавитьtitle, название выберете самостоятельно любое. Я написал просто location. Свойство frameborder="0" удалим совсем.

src/Components/Location/index.js полностью
import React from "react";

const Location = () => {
  return (
    <div className="location_wrapper">
      <iframe
        src="https://www.google.com/maps/embed?pb=!1m18!1m12!1m3!1d2435.290628985655!2d4.918337915802386!3d52.383281579788566!2m3!1f0!2f0!3f0!3m2!1i1024!2i768!4f13.1!3m3!1m2!1s0x47c609abb3be496b%3A0x1a140c53c426f7c7!2sthe+Kromhouthal!5e0!3m2!1sen!2sua!4v1559491565118!5m2!1sen!2sua"
        width="100%"
        height="500px"
        allowFullScreen
        title="location"
      />
      <div className="location_tag">
        <div>Location</div>
      </div>
    </div>
  );
};

export default Location;



Идем в основной файл - App.js подключаем

import Location from './Compomemts/Location';

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

В итоге у нас получится вот так:



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Location section"


Подвал сайта - футер

На этом я остановлюсь коротко. У нас будет самый простой подвал, с самой простой разметкой. Пэтому мы создадим его в папке, которую создали еще в первой части, и в которой у нас все для хэдера-фуера.

Наша задача будет просто вывести разметку и задать ей некую задержку проявления - Fade.Ранее мы уже работали с этим эффектом и потому я останавливаться на этом подробно не буду.

src/Components/Header_Footer/Footer.js
import React from "react";
import Fade from "react-reveal/Fade";

const Footer = () => {
  return (
    <footer className="bck_red">
      <Fade delay={500}>
        <div className="font_righteous footer_logo_venue">
          wmg react-meeting
        </div>
        <div className="footer_copyright">
          The venue 2019. © All rights reserved.
          <br />
          Made by Kolesnikov Yaroslav
        </div>
      </Fade>
    </footer>
  );
};

export default Footer;



Добавляем стили:

resources/style.css
/* ====================>>> FOOTER <<<========================= */
footer {
    padding:50px 0px;
    text-align: center;
    font-size: 60px;
    color:#ffffff;
    background: red;
}

footer .footer_copyright {
    font-size: 18px;
}



Подключаем и выводим компонент на страницу - как обычно в файле App.js



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Footer section"




                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer

четверг, 16 мая 2019 г.

React-Сайт ( II ). Инфо-основная секция.

Не отступая от выбранной нами концепции написания сайта легкими - функциональными компонентами без состояния, мы сегодня создадим секции сайта несущие основную информацию, сделаем отдельным компонентом многоразового использования - кнопку и много других интересных вещей.




Все статьи этого проекта:




Мы зададим анимацию -плавное, последовательное проявление верхних информационных блоков и "прокрутка" скидки на билет. Если посмотреть на готовое решение, то наши секции должны выглядеть так.



Секция Инфо

В папке Components создадим папку Info, а в ней файл - index.js, в котором создадим react stateless components (rsc) компонент без состояния.

Так, как вверху у нас будут иконки, то мы импортируем их в наш компонент.Иконки я подготовил заранее и поместил их в отдельную папку в папке resources/img/icons

import icon_calendar from '../../resources/img/icons/calendar.png';
import icon_location from '../../resources/img/icons/location.png';

Так как стилизация компонентов, да и приложения в целом, не является целью данного поста, то я для стилизации этих секций воспользуюсь готовыми стилями, которые приведу ниже. Вы же можете написать их самостоятельно

src/resources/style.css
body {
    padding:0;
    margin:0;
    font-family: 'Roboto', sans-serif;
}

.font_righteous {
    font-family: 'Righteous', cursive;
}

.bck_black {
    background: #3c3c3c;
}
.bck_white {
    background: #ffffff;
}
.bck_red {
    background: #ff4800;
}
.bck_yellow {
    background: #ffa800;
}

.iconImage {
    width: 20px;
    margin-right: 10px;
}

.center_wrapper {
    width: 900px;
    margin:0 auto;
}
/* ===============>>> HEADER <<<================ */
header {
    padding:10px 0px;
    transition: all 300ms ease-in;
}

header .header_logo {
    flex-grow: 1;
}
header .header_logo_title {
    text-transform: uppercase;
    font-size: 40px;
}

header .header_logo_caption {
    text-transform: uppercase;
}

/* =================>>> SLIDER <<<================== */
.event_name {
    position: absolute;
    top: 50%;
    left: 50%;
    width: 400px;
    height: 160px;
    -webkit-transform: translate(-50%, -50%);  
    transform: translate(-50%, -50%);   
    border: 2px solid white;
}

.event_name .wrapper {
    border: 2px solid white;
    height: 121px;
    margin: 6px;
    text-align: center;
    color: #fff;
    font-size: 40px;
    padding: 21px 70px 0px 70px;
    text-transform: uppercase;
}
.carrousel_wrapper {
    height:700px;
    overflow: hidden;
}

.carrousel_image {
    
    background-size: cover !important;
}

/*===================>>>  INFO <<======================*/

.vn_wrapper {
    display: flex;
    padding: 170px 0px;
}

.vn_item {
    width:50%;
    color:#ffffff;
    text-align: center;
}
                      
.vn_item .vn_outer {
    width: 300px;
    border: 2px solid #828282;
    margin: auto;
    padding: 5px;
}

.vn_item .vn_inner { 
    border: 2px solid #828282;
    position: relative;
    padding: 0px 20px;
    transition: all 500ms ease;
}

.vn_item .vn_inner:hover {
    background: #3e3e3e;
}

.vn_item .vn_icon_square {
    width: 100px;
    height: 100px;
    position: absolute;
    -ms-transform: rotate(45deg);
    -webkit-transform: rotate(45deg);
    transform: rotate(45deg);
    top: -54px;
    left: 100px;
}

.vn_item .vn_icon {
    width: 50px;
    height: 50px;
    background-size: contain !important;
    background-repeat: no-repeat !important;
    position: absolute;
    top: -30px;
    left: 124px;
}

.vn_item .vn_title {
    font-size: 28px;
    margin: 90px 0px 20px 0px;
    border-bottom: 1px solid #5d5d5d;
    padding-bottom: 10px;
}

.vn_item .vn_desc {
    font-size: 30px;
    font-weight: 300;
    margin-bottom: 90px;
}

/*==============>>> HIGHLIGHT <<<======================*/


.highlight_wrapper h2 {
    text-align: center;
    text-transform: uppercase;
    color:#2c2c2c;
    font-size: 52px;
}

.highlight_wrapper .highlight_description {
    line-height: 30px;
    font-size: 18px;
    font-weight: 300;
    border-top: 1px solid #dddddd;
    border-bottom: 1px solid #dddddd;
    padding: 30px 0px;
}

.discount_wrapper {
    display: flex;
    padding: 70px 0px;
}

.discount_wrapper .discount_porcentage {
    padding-right: 100px;
}

.discount_wrapper .discount_porcentage span:nth-child(1) {
    color: #ff4800;
    font-size: 125px;
    font-weight: 400;
    display: block;
    line-height: 120px;
}

.discount_wrapper .discount_porcentage span:nth-child(2) {
    color: #2c2c2c;
    font-size: 50px;
    font-weight: 400;
    display: block;
}


.discount_wrapper .discount_description h3 {
    font-size: 34px;
    font-weight: 100;
    margin: 0px;
    color: #2c2c2c;
}

.discount_wrapper .discount_description p { 
    line-height: 30px;
    font-size: 18px;
    font-weight: 300;
}


В компоненте Info просто добавим класс диву и выведем его название. Подключим его в App копоненте и проверим на экране.

src/Components/Info/index.js
import React from "react";
import icon_calendar from "../../resources/images/icons/calendar.png";
import icon_location from "../../resources/images/icons/location.png";
const Info = () => {
  return <div className="bck_black">Info</div>;
};

export default Info;



Теперь уберем инлайновый стиль для компонента App, чтоб не мешал и добавим в Info/index.js разметку. Здесь все как обычно. Остановлюсь только на том, что мы используем уже установленный пакет react-reveal его возможность - Zoom для плавного вывода отдельного блока. Второму блоку придали задержку, для того, чтобы показывать их последовательно.

src/Components/Info/index.js
import React from "react";
import Zoom from "react-reveal/Zoom";

import icon_calendar from "../../resources/img/icons/calendar.png";
import icon_location from "../../resources/img/icons/location.png";
const Info = () => {
  return (
    <div className="bck_black">
      <div className="center_wrapper">
        <div className="vn_wrapper">
          <Zoom duration={500}>
            <div className="vn_item">
              <div className="vn_outer">
                <div className="vn_inner">
                  <div className="vn_icon_square bck_red" />
                  <div
                    className="vn_icon"
                    style={{
                      background: `url(${icon_calendar})`
                    }}
                  />
                  <div className="vn_title">Event Date & Time</div>
                  <div className="vn_desc">7 August 2017 @10.00 pm</div>
                </div>
              </div>
            </div>
          </Zoom>

          <Zoom duration={500} delay={500}>
            <div className="vn_item">
              <div className="vn_outer">
                <div className="vn_inner">
                  <div className="vn_icon_square bck_yellow" />
                  <div
                    className="vn_icon"
                    style={{
                      background: `url(${icon_location})`
                    }}
                  />
                  <div className="vn_title">Event Location</div>
                  <div className="vn_desc">
                    345 Speer Street Oakland, CA 9835
                  </div>
                </div>
              </div>
            </div>
          </Zoom>
        </div>
      </div>
    </div>
  );
};

export default Info;



В итоге мы получаем полностью готовую первую секцию с плавным и последовательным выводом на экран двух ее блоков с иконками.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Info section"


Основная секция

Сейчас перейдем к созданию основной секции. Если посмотреть на нее, то мы сможем легко и просто разделить её на несколько компонентов.

Для работы, и чтобы в дальнейшем задать отдельную анимацию, мы разобьем этот блок на два больших компонента - Description (на фото желтым) и Discount (зеленым)

Кнопку (красным на фото)создадим отдельным компонентом, который будет многократно использован в этом приложении, поэтому мы можем вынести её в отдельную папку, но подключим ее в файле компонента Discount



Все эти компоненты мы соберем в отдельный файл index.js и уже его подключим и выведем в основной компонент App

Для компонентов Description и Discount и этого файла index.js создадим отдельную папку и назовем ее Highlights

Сразу в ней создадим файл index.js

src/Components/Highlight/index.js
import React from "react";
import Description from "./Description";


const Highlights = () => {
  return (
    <div className="highlight_wrapper">
      <Description />
    </div>
  );
};

export default Highlights;



Пока что здесь нет ничего интересного, просто мы выводим компонент Description, который сейчас и создадим.

src/Components/Highlight/Description.js
import React from "react";
import Fade from "react-reveal/Fade";

const Description = () => {
  return (
    <Fade>
      <div className="center_wrapper">
        <h2>Highlights</h2>
        <div className="highlight_description">
          Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do
          eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad
          minim veniam, quis nostrud exercitation ullamco laboris nisi ut
          aliquip ex ea commodo consequat. Duis aute irure dolor in
          reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla
          pariatur. Excepteur sint occaecat cupidatat non proident, sunt in
          culpa qui officia deserunt mollit anim id est laborum.
        </div>
      </div>
    </Fade>
  );
};

export default Description;



Этот компонент просто выводит текст и разметку. Из интересного есть то, что мы добавили плавное появление его на странице, с помощью все того же пакета react-reveal.

На странице это будет выглядеть так.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Description component"


Счетчик скидки - Discount- компонент.

В этой части у нас будет один компонент, который будет содержать текст и счетчик скидки, который при загрузке (первой прокрутке) будет динамически меняться и останавливаться на определенном значении. Тесктовый блок не представляет интереса - это простая разметка. Единственное что, так это то , что мы добавим плавное появление.

Поэтому, хотя и можно условно (да и не только условно :-)) разделить этот компонент на два, я не хочу создавать отдельный компонент для разметки и просто помещу его в вывод одного компонента. Для себя, вы можете сделать иначе. Таким образом у вас получится счетчик для многоразового использования.

Создадим компонент Discount

src/Components/Highlight/Discount.js
import React, { useState, useEffect } from "react";
import Fade from "react-reveal/Fade";
import Slide from "react-reveal/Slide";

const Discount = () => {
  const [discountStart, setDiscountStart] = useState(0);
  const discountEnd = 30;

  const porcentage = () => {
    if (discountStart < discountEnd) {
      setDiscountStart(discountStart + 1);
    }
  };

  useEffect(() => {
    setTimeout(() => {
      porcentage();
    }, 30);
  }, [discountStart]);

  return (
    <div className="center_wrapper">
      <div className="discount_wrapper">
        <Fade onReveal={() => porcentage()}>
          <div className="discount_porcentage">
            <span>{discountStart}%</span>
            <span>OFF</span>
          </div>
        </Fade>

        <Slide right>
          <div className="discount_description">
            <h3>Purchase tickets before 20th JUNE</h3>
            <p>
              Sed ut perspiciatis unde omnis iste natus error sit voluptatem
              accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
              quae ab illo inventore veritatis et quasi architecto beatae vitae
              dicta sunt explicabo.
            </p>
          </div>
        </Slide>
      </div>
    </div>
  );
};

export default Discount;



В этом файле самая интересная часть это счетчик, который выводит последовательность цифр. Это достигается тем, что мы меняем значение переменной "состояния" discountStart в сторону увеличения на единицу.

Каждый раз при изменении переменной "состояния" discountStart у нас обновится наш компонент, потому что мы передали эту переменную, как массив в хук useEffect вторым аргументом. Подробнее я писал об этом в первой части.

Функцию, которая запустить это процесс мы поместили в Fade для того, чтобы отсчет начался с момента показа пользователю этого компонента.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Discount component"


Кнопка

Её мы создадим, как и договорились ранее, в отдельной папке в компонентах. Назовем ее произвольно, например Utils MyButton.js. Так как она будет использоваться многократно, то мы будем в нее передавать стили и линк, по которому будет осуществляться переход, в виде props. Сама же кнопка будет иметь иконку, которую также будет получать от родителя.

В данном случае, дольше рассказывать, чем показать.

src/Components/Utils/MyButton.js

import React from "react";
import Button from "@material-ui/core/Button";

import TicketIcon from "../../resources/img/icons/ticket.png";

const MyButton = props => {
  return (
    <Button
      href={props.link}
      variant="contained"
      size="smal"
      style={{
        background: props.bck,
        color: props.color
      }}
    >
      <img src={TicketIcon} className="iconImage" alt="icon_button" />
      {props.text}
    </Button>
  );
};

export default MyButton;



Эту кнопку мы поместим в компонент Discount и передадим ей нужные свойства -props

src/Components/Highlight/Discount.js
import React, { useState, useEffect } from "react";
import MyButton from "../Utils/myButton";
import Fade from "react-reveal/Fade";
import Slide from "react-reveal/Slide";

const Discount = () => {
  const [discountStart, setDiscountStart] = useState(0);
  const discountEnd = 30;

  const porcentage = () => {
    if (discountStart < discountEnd) {
      setDiscountStart(discountStart + 1);
    }
  };

  useEffect(() => {
    setTimeout(() => {
      porcentage();
    }, 30);
  }, [discountStart]);

  return (
    <div className="center_wrapper">
      <div className="discount_wrapper">
        <Fade onReveal={() => porcentage()}>
          <div className="discount_porcentage">
            <span>{discountStart}%</span>
            <span>OFF</span>
          </div>
        </Fade>

        <Slide right>
          <div className="discount_description">
            <h3>Purchase tickets before 20th JUNE</h3>
            <p>
              Sed ut perspiciatis unde omnis iste natus error sit voluptatem
              accusantium doloremque laudantium, totam rem aperiam, eaque ipsa
              quae ab illo inventore veritatis et quasi architecto beatae vitae
              dicta sunt explicabo.
            </p>
            <MyButton
              text="Purchase tickets"
              bck="#ffa800"
              color="#ffffff"
              link="http://google.com"
            />
          </div>
        </Slide>
      </div>
    </div>
  );
};

export default Discount;



Вся прелесть работы с такими компонентами состоит в том, что мы создаем его один раз, а потом можем передать в него иные свойства и отобразить его в другом месте и с несколько иной функцией.



Все файлы проекта на этом этапе смотрите в репо - react-site-slider-abc
ci -m "Button Component"


                                                                                                                                                             

Телеграм канал - Full Stack JavaScript Developer


Хотите освоить самые современные методы написания React приложений? Надоели простые проекты? Нужны курсы, книги, руководства, индивидуальные занятия по React и не только? Хотите стать разработчиком полного цикла, освоить стек MERN, или вы только начинаете свой путь в программировании, и не знаете с чего начать, то пишите через форму связи, подписывайтесь на мой канал в Телеге, вступайте в группу на Facebook.Пишите мне - kolesnikovy70 почта gmail.com