React on λambda


Без особых церемоний начнем раздавать лещей и кричать о функциональщине, да кстати, всем привет!


С развитием реакт плавно избавляется от ООП-шной примеси и всё больше приближается к функциональному программированию. В начале в нем появились компоненты высшего порядка (HOC) вместо миксинов, затем stateless компоненты почти замена классам, и вот последний рывок, выкатили хуки (hooks), которые полностью избавляют реакт от классов.


Не знаю куда приведет следующая ветвь развития, но уверенно могу сказать, пора уже избавляться от JSX, и да в пользу тех самых функций. Будь с нами Сергей Дружко, то мы бы услышали:


— Сильное заявление, проверять я его конечно не буду.


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



Королевство шаблонов


В мире шаблонов всё крутится вокруг его величества текста, вы погружаетесь в него по самые уши и приступаете к грязным делам, а именно вставляете код, указывая где нужно повторить, а где что-то показать или скрыть. А чтобы обозначить границу между текстом и кодом используются усики (фигурные скобки), теги или еще какие-нибудь директивы.




В отличие от шаблонизаторов, в JSX переход из кода в HTML, происходит автоматически без разметки и эти переходы можно повторять рекурсивно. Именно поэтому можно услышать критику в сторону JSX, что мол вы кодируете JS внутри HTML, а внутри того JS другой вложенный HTML и т.д.


Ну а в остальном же JSX это тот же шаблонизатор, а всё потому, что изначально на реакт оказал свое влияние XHP, по сути это тюнингованный фейсбуком PHP. Если реакт в чистом виде мало, что имеет общего с XHP, то JSX его брат близнец, но только в мире JavaScript.


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


Ведь сейчас вы получаете HTML + CSS от верстальщика/дизайнера, быстро вставили туда усики или директивы и аля компонент готов, а то и гляди вся страница. Да, безусловно тут фреймворки типа Vue/Angular выруливают и тихо плакал наш реакт в стороне. К сожалению, на практике я никогда не встречал дизайнера, который предоставлял HTML + CSS, а верстальщиком был некий мифический персонаж, которого никто не встречал в жизни, а в жизни многих компаний даже дизайнеры в штате — это выдуманные существа, и всю эту работу делает, правильно — фронтэндер. Именно поэтому часто в требованиях на работу мы встречаем подобное:


— Опыт работы на Bootstrap восьмой версии не менее 10 лет.


Если это ваш случай, то нет там разницы: верстать в начале HTML с усиками или сразу бацать компонент на чистых функциях. Хотя конечно, есть разница с функциями придется меньше стучать по клавишам.


Королевство функции


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


Однако в этом мире не все функции равны, есть обычные функции дворняги, а есть вельможи — каррированые функции, видимо сам сэр Карри Хаскелл даровал им этот титул.


Далее в примерах, я буду использовать библиотеку react-on-lambda от некого автора — меня, но вам ничего не мешает создать свой велосипед.


Окей, давайте посмотрим на этих вельмож:


import λ from 'react-on-lambda'
const postLink = λ.a({href: `/posts/123`})

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


К примеру так:


postLink(`Read more`)
// JSX equivalent
<a href=”/posts/123”>Read more</a>

Ах, да вас могла смутить греческая буква: λ просто проигнорируйте, ее можно заменить на любой другой идентификатор к примеру:


import l from 'react-on-lambda'
// or 
import {div, h1} from 'react-on-lambda'

Думаю такие причуды встречаются не впервые в js, для нас уже как родные символы $ _, казалось бы какая связь с баксами и либой для манипуляции DOM. А лямбда пришлась мне по вкусу, так как она перекликается с названием самой либы.


И так в ходе выполнения программы, свойства элементов/компонентов можно собирать из разных кусочков, не прибегая к глобальным переменным, а главное можно строить point-free композиции:


const title = λ.compose(
  λ.h1({className: `post-title`}),
  postLink
)

const post = λ.div(
  title(`How to use react on lambda?`),
  λ.p(`
    Lorem ipsum dolor sit amet,
    Ernestina Urbanski consectetur adipiscing elit.
    Ut blandit viverra diam luctus luctus...
  `),
  postLink(`Read more`)
)

render(
  post,
  document.getElementById(`app`)
)

С помощью композиции, мы создали новую функцию title, которая состоит из двух других функций h1 и postLink. Передав значение в title мы получим кликабельный заголовок с текстом: "How to use react on lambda?". В композиции результат от одной функции передается в другую, причем поток данных происходит снизу-вверх.




Благодаря этой фишке, функции в композиции размещаются без вложенностей. Вспомните callback-и до появления Promise и async/await, как они напрягали, и как их только не обзывали: спагетти код, callback hell, pyramid of doom, christmas tree from hell, однако многоэтажные вложенности в HTML почему то никого не смущают.


Далее мы еще раз применили postLink, но уже с другим параметром, таким образом функцию мы использовали неоднократно. Безусловно, такое можно провернуть с JSX, завернув его в функцию, но тогда мы придём к главному вопросу, а может просто использовать только функции вместо JSX?



Королевство React on λambda


Скорее это не королевство, а маленькое графство в королевстве функций. Предлагаю поближе познакомиться с React on lambda:


Основные фичи библиотеки:


  • на выходе получется размер бандла меньше, аж до 20% в сравнении с аналогичным проектом написанный на JSX;
  • не требуется транспайлер (babel) или отдельной настройки webpack, работает прямо в браузере;
  • плавная интеграция в существующий реакт проект с JSX.

Для более детального ознакомления предлагаю посмотреть на демо проекты:




Креационизм в RoL


Чтобы создать реакт элемент достаточно набрать:


import λ, {div} from 'react-on-lambda'

div({class: `sample`}, `Hello world!`) // you can use class instead className
// JSX equivalent
<div className=”sample”>Hello world!</div>

Свойства можно перекрывать:


const span = λ.span({class: `large`}) // -> function

span({class: `small`}, `Sorry we changed our mind`)
// JSX equivalent
<span className="small">Sorry we changed our mind</span>

Существующие компоненты достаточно обернуть λ, чтобы получить из них функцию и все плюшки ФП.


λ(Provider, {store}, app)
// JSX equivalent
<Provider store={store}><App/></Provider>

Все дочернии лямбда функции будут вызваны автоматически родительским элементом:


λ.div(
  λ.div({class: `followers`}),
  λ.br
)

То есть не обязательно их вызывать:


λ.div(
  λ.div({class: `followers`})(),
  λ.br()
)()

Это было сделано для удобства и простоты интеграции с другими библиотеками, такими как redux.


А дальше я бегло познакомлю вас с другими вспомогательными функциями. Хочу напомнить, что все подданные из react-on-lambda являются каррироваными функциями.



λ.mapKey


Функция mapKey служит для перебора массивов.


const pages = [`Home page`, `Portfolio`, `About`]

λ.ul(
  λ.mapKey(λ.li, pages)
)

// JSX equivalent
<ul>
  {pages.map((item, key) =>
    <li key={key}>
      {item}
    </li>
  )}
</ul>

Вставка ключа (key) будет автоматической и будет равна индексу элемента из массива. Автоматическая вставка ключа будет только если не был передан ключ.



λ.mapProps


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


const data = [
  {id: 123, name: `Albert`, surname: `Einstein`},
  {id: 124, name: `Daimaou `, surname: `Kosaka`},
]

const userList = λ.compose(
  λ.div({class: `followers`}),
  λ.ul,
  λ.mapKey(λ.li),
  λ.mapProps({key: `id`, children: `name`})
)

userList(data)

// JSX equivalent
const UserList = props => (
  <div className="followers">
    <ul>
      {props.data.map(user =>
        <li key={user.id}>
          {user.name}
        </li>
      )}
    </ul>
  </div>
)

<UserList data={data}/>


λ.log


Функция для отладки:


const userList = λ.compose(
  λ.div,
  λ.ul,
  λ.log(`after mapping`), // -> will log piping value
  λ.mapKey(λ.li)
)

Стилизация компонентов


Для любителей styled-components, есть встроенная обертка, которая возвращает стилизованный компонент в виде функции:


import λ from 'react-on-lambda'

const header = λ.h1`
  color: #ff813f;
  font-size: 22px;
`

const onClick = () => alert(`Hi!`)

const app = λ.div(
  header(`Welcome to React on λamda!`),
  λ.button({onClick}, `OK`)
)

Я намерено не стал пичкать библиотеку другим функционалом, так как множество фишек можно получить из библиотек: ramda, rambda, lodash/fp.


Ну на этом всё, буду рад вашим отзывам.



Берегите себя, да пребудет с вами святой функтор!

Поделиться публикацией

Похожие публикации

Комментарии 11

    0

    На первый взгляд, получается, что вы переизобрели и упростили изначальный синтаксис React. Именно то, во что компилируется JSX: React.createElement('div', props, content);


    Выглядит, впрочем, забавно. Особенно, если бы на клавиатуре была клавиша с лямбдой ;)

      0
      Совершенно верно, при этом добавил немного специи и карри.
      Лямбда добавлена больше для фана, делать так совсем необязательно. Ну а если так хочется ее использовать, несложно настроить клавиатуру.
      0
      а если сделать из нее jsx pragma плагин то можно писать тоже самое декларати… oh wait.

      Короче — миксанули recompose с React.createElement..., получилось забавно.
      А можно какой-то реальный юз-кейс что бы понять преимущество над jsx + recompose? хотя бы в плане размера бандла?
        0

        В этом проекте rol-vs-jsx вы найдете сравнение бандлов и замеры по скорости рендера.


        В статье я упомянул о демо-проектах:



        Кстати, попробуйте собрать bundle локально (npm run build) — конечный размер будет не более: 7KB.


        Но конкретно с recompose не стал делать, так как с появлением хуков (hooks) актуальность либы потерялась.


        В RoL скорее вы будете делать не HOC, а HOF.

        0

        Из далека похоже на Elm или Fable.

          +1
          Ещё немного, и вы изобретёте LISP.
            0
            Подход, в котором доминирует скрипт а не разметка — правильный, но если ваши функции заменить методами класса — получим то же самое, но с возможностью наследования. Рано ООП списывать.
              0
              Забавно следить за тем, как «функциональшики» пытаются избавить мир программирования от OOP, как они всем рьяно доказывают, что функциональное программирование это круто и модно, но всем на это наср*ть.

              Эту забавную картину вижу уже не первый год :)
                0

                Весьма интересно наблюдать, как все те практики, которые 15+ лет назад в контексте пхп считались эталонным примером дна, теперь bleeding edge фронтенд разработки.
                С другой стороны — понятно, куда это будет развиваться в дальнейшем. Точно так же, как на пыхе от неподдерживаемых ворохов начали отказываться в пользу нормальных фреймворков — так и на фронте это все случится со временем, т.к. приложения усложняются и те методы, которые были удобны для того, чтобы быстро накалякать read-only код для гостевухи, в 2019 все чаще работать перестают.
                С-но, ведь и php тут был не первым — описываемым в статье подходам уже более полувека, так что это уже даже не второй этап переизобретения велосипедов.

                  0
                  Это стандартная ошибка считать jsx — шаблоном. Это не так, это js код, просто обернутый в «читаемый» псевдо-xml для удобства. Он не обязателен, он просто упрощает понимание ментальной модели компонента.
                    +1
                    Это стандартная ошибка считать jsx — шаблоном. Это не так, это js код, просто обернутый в «читаемый» псевдо-xml для удобства.

                    Ну так я ж про это и говорю — без шаблонов на пыхе и писали. Просто с обычным пых-кодом и обычными пых-функциями, которые плевались текстом через echo :)
                    Это был 100% реакт как он сейчас есть, поверьте, все те же ощущения, тот же flow :)
                    И в фейсбуке именно так писали — для удобства только XHP запилили, чтобы не теребить голый текст. А потом кому-то пришла в голову гениальная идея портировать это протухшее легаси на фронт, вместо того чтобы выбросить и переписать по-человечески :)

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

                Самое читаемое