Skip to content

Latest commit

 

History

History
218 lines (154 loc) · 13.6 KB

06. Компонентный подход на примере styled-components и react-router.md

File metadata and controls

218 lines (154 loc) · 13.6 KB

Компонентный подход на примере styled-components и react-router

Разберем компонентный подход на примере двух наиболее известных библиотек в Реакте

Помните, в прошлых уроках мы говорили про то, что Реакт силён компонентным подходом? Мы ещё долго будем это разбирать, но начнём сейчас.

Стайлед-компонентс

Знаете, раньше был хайп по CSS-in-JS, люди изобретали всякие JSS и прочие Афродиты, но все они были неудобными: да, прикольно иметь мощь Джс вместо настройки постцсс-плагинов, но за это приходилось расплачиваться удобством — посмотрите на этот синтаксис.

import React from "react";
import injectSheet from "react-jss";

const styles = {
  button: {
    background: props => props.color
  },
  label: {
    fontWeight: "bold"
  }
};

const Button = ({ classes, children }) => (
  <button className={classes.button}>
    <span className={classes.label}>{children}</span>
  </button>
);

export default injectSheet(styles)(Button);

Честно говоря, даже я долгое время не был впечатлён этим программированием объектами и обходился ЦСС-модулями, но однажды летом 2017 случилось две вещи: я увидел твитшторм Джеймса Кайла и стайлед-компонентс.

Какая основная проблема ЦСС? В том, что во времена приложений мы зачем-то занимаемся AOP (Aspect Oriented Programming): описываем селекторы чтобы прицепить к компоненту (у нас же везде компонентный подход).

Эндрю Стальтц нарисовал диаграммы с этими тремя разными подходами:

Видите победителя? Да, стайлед-компонентс оказался самым живучим и удобным. Ведь зачем делать .header--nav-____---_---button когда можно сразу написать компонент и его стили?

Сравните:

// подобие БЭМ-а
import "./headerStyles.css";

// стайлед-компонентс
import styled from "styled-components";

const LogOutButton = styled.button`
  color: #fff;
  border: 1px solid #e3e3e3;
  // etc
`;

function Header() {
  return (
    <header>
      <button className="header--nav--button">Log In</button>
      <LogOutButton>Log Out</LogOutButton>
    </header>
  );
}

Ого-го, это что за магия? Это стайлед-компонентс: наш первый босс, демонстрирующий компонентный подход.

Как с этим работать? Да просто: используете функцию styled(component) чтобы застилизовать компонент. Как это работает? Автор, Макс Стойбер, рассказал в своём блоге: The magic behind 💅 styled-components. За это отвечает template literals — фича из ES2015 (сам Экмаскрипт мы ещё в будущем разберём).

Почему я написал styled(component), а в примере выше styled.button? Потому что тут тот же принцип, что и в Реакте:

// Джсх
<h1>Заголовок</h1>;

// createElement
React.createElement("h1", null, "Заголовок");

Чтобы не грузить людей всякими import { h1 } from 'React' или import { button } from 'styled-components', у Реакта и Стайлед-компонентс есть так называемые шорткаты (или альясы): эти библиотеки сами разбираются с дефолтными тегами ХТМЛ.

Пропы в СК

Окей, едем дальше. Одна из важных фич СК это работа с пропами: вы передаёте проп в СК, а в самом СК — читаете его.

import styled, { css } from styled-components;

const Button = styled.button`
  display: inline-block;
  border-radius: 3px;
  padding: 0.5rem 0;
  margin: 0.5rem 1rem;
  width: 11rem;
  background: transparent;
  color: white;
  border: 2px solid white;

  ${props => props.kind === 'primary' && css`
    background: white;
    color: palevioletred;
  `}
`;

<Button>Обычная кнопка</Button>
<Button kind="primary">Розовая кнопка</Button>

На этом моменте я вас должен отпустить читать официальную документацию: ни один курс не объяснит лучше оригинала, а вам нужно учиться добывать информацию самому, если вы ещё не, а пока я отвечу на пару не заданных вопросов.

Неужели можно хранить всё вместе? И стили, и отображение

Можно, почему нет? У нас же компонентный подход, нам нет нужды разделять стили, логику и отображение, поэтому писать в одном файле и СК и их рендер — нормально.

Но если вам кажется что слишком много компонентов, то выделите их в файл styled.js и положите в ту же директорию, что и компонент: например,

/src
- /Header
-- index.js
-- Search.js
-- styled.js

А как писать логику? Или стилизовать существующий компонент?

В будущем мы будем проходить стейты и классы, но отвечу на вопрос уже сейчас: стилизовать существующие компоненты можно, СК передаёт в них проп className со стилями, поэтому у компонента он должен быть.

// допустим, из другой библиотеки
function Header(props) {
  return (
    <header className={props.className}>
      <nav>...</nav>
      <button>...</button>
    </header>
  );
}

const StHeader = styled(Header)`
  background-color: #000;
`;

// если нет чилдрена, то можно тег сразу закрыть
<StHeader />;

Вот и всё знакомство с СК! Оцените красоту: больше не нужно писать стили в отдельном файле и работать с селекторами, если можно сразу писать компоненты и использовать их.


Кстати, Стайлед-компонентс, как и Реакт-роутер, ставятся через ярн: yarn add styled-components.

Реакт-роутер

Реакт-роутер иногда не любят: ещё бы, к четвёртой версии (вы помните про Семвер?) ребята сломали АПИ раза три, причём последний был настолько жёстким, что впору было называть отдельным продуктом: не было даже Upgrade Guide, который принят при смене версий. Реакт-роутер сильно поменялся и стал Реакт-роутером. И дело вот в чём.

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

Например, если мы находимся на странице /courses/react, то нужно отобразить страницу курса по Реакту, если на /courses — список курсов, а на / — главную. Давайте попробуем написать псевдокод для этого? Например, заведём компонент <Route />, который будет отображать компонент в зависимости от пропов.

Для этого мы будем работать с window.location — глобальным объектом, который даёт браузер. В нём есть много разных свойств, нас интересует pathname — текущий путь без домена.

В пропе path мы указываем этот путь, а в component — какой компонент нужно отрендерить.

function Route(props) {
  // есть ли в текущем `pathname` путь, указанный в `props.path`
  // .includes это метод у String, строки
  // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/includes
  if (window.location.pathname.includes(props.path)) {
    // мы не можем сделать <component />,
    // потому что Реакт подумает что это хтмл-тег,
    // поэтому мы воспользуемся React.createElement()
    React.createElement(props.component, null, null);
  }
}

function App() {
  return (
    <section>
      {/* хедер будет рендериться на всех страницах */}
      <Header />

      <Route path="/courses/react" component={Main} />
      <Route path="/courses" component={Main} />
      <Route path="/" component={Main} />
    </section>
  );
}

Поздравляю! Мы написали Реакт-роутер. Я не шучу, его философия динамического роутинга именно в этом и заключается: отрендерь компонент (component) здесь, если путь (path) сходится. Правда, у него есть ещё проп exact={true}, чтобы рендерить только если жёсткое соответствие: у нас в примере на странице /courses/react отрендерятся все три компонента.

Зацените, какая простая и крутая идея: использовать компонент, чтобы отрендерить другой, если путь сошёлся.

Реакт-роутер, на самом деле, чертовски мощный и вам нужно о нём знать три компонента: <BrowserRouter />, <Route /> и <Link />. Ещё у него есть <Redirect /> и немного других — прочтите документацию, а пока: корректно работающая демка. Кстати, эту демку я бесстыже скопировал из той же документации, раздел Examples.

iframe: https://codesandbox.io/embed/01650wwoxv

(синяя кнопка — редактор кода, бежевая — превью)

Итог

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

Кстати, думаю у вас есть вопрос — а оправдывает ли себя такой подход на больших проектах? Ведь он такой простой, не сложно ли потом поддерживать, когда нужно больше фич? Нет, потому что легко сделать сложно и сложно сделать легко — командам Реакта, СК и Реакт-роутера приходится сильно стараться, чтобы выдавать такие простые продукты.