Без особых церемоний начнем раздавать лещей и кричать о функциональщине, да кстати, всем привет!
С развитием реакт плавно избавляется от ООП-шной примеси и всё больше приближается к функциональному программированию. В начале в нем появились компоненты высшего порядка (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.
Ну на этом всё, буду рад вашим отзывам.
Берегите себя, да пребудет с вами святой функтор!
Автор: sultan99