Your browser doesn't support all the features required, so you are presented with a simplified version of this presentation.

For the best experience please use the latest Chrome browser.

React, Flux
и правильные абстракции

Me

Илья Сегеда
aka ALFer

Работа программиста - это взять одни абстракции, объеденить их c другими абстракциями и получить третьй абстракции


© Sebastian Markbåge

MapReduce позволил сделать Hadoop. С помощью Hadoop показал как большие сайты обрабатывают их данные.
Dynamo описал как писать ДБ чтоб они могли работать вместе и скейлились с появлением новых нод и кластеров. Отсюда выросли Cassandra и Riak.
Twitter потом свитчнулся обратно. Но это лишь из-за не отполированности реализации идеи. Тогда же появляется Backbone.js, что способствовало росту количества приложений с такой архитектурой. (В 2011 появляется Bootstrap)
Angular.js структурировал приложения лучше Backbone.js, повысил их тестируемость и позволил шире использовать best practices, скрыв нудятину вроде binding data и rendering

Немного истории

В 2013 Pete Hunt выступает с докладом
Rethinking Best Practices и делает ещё одну революцию

React

Как бы нам хотелось

Data

Library

DOM


Library(Data) → DOM

Объеденим логику отображения и само отображение

Но как же separation of concerns?

Объеденим логику отображения и само отображение

Но как же separation of concerns?

Темплейты разделяют не concerns, а технологии

Объеденим логику отображения и само отображение

Но как же separation of concerns?

Темплейты разделяют не concerns, а технологии

А надо уменьшить coupling и увеличить cohesion

Объеденим логику отображения и само отображение

Но как же separation of concerns?

Темплейты разделяют не concerns, а технологии

А надо уменьшить coupling и увеличить cohesion

Они неразделимы. Они творят UI

React представляет компоненты

Это абстракция над логическим блоком вашего приложения

Они очень слабо связанны между собой (coupling)

Они отлично выполняют одну задачу (cohesion)

Вы пишете на JS, а не на templating language

Они являются first-class sitizen'ами для языка

React ререндерит всё дерево

Вспомним как было в 90ые с server-rendering'ом. Каждая страница была для браузера декларитивным описанием, что нужно показать

React декларативно описывает как должен выглядеть наш UI в конкретный момент времени

React ререндерит всё дерево

А чтоб это не тормозило была придумана концепция Virtual DOM

React ререндерит всё дерево

Game state
Game logic
Scene IR
OpenGL ops
GFX card

React ререндерит всё дерево

App state
React
components
Virtual DOM
DOM
operations
Browser

React представляет JSX

Он не обязателен

Он удобен

Вы пишете HTML на JS

<a href="http://odessajs.org/">OdessaJS FTW!</a> React.DOM.a({ href: "http://odessajs.org/" }, "OdessaJS FTW!")

Дополнительные плюсы React

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

React может рендерить ваше приложение в просто строку. Тоесть, его можно запустить на сервере. И это большой шаг к изоморфности

Flux

Возникла проблема

Как нам структурировать приложение так чтоб работа с данными была такой же удобной как и работа с компонентами?

View
Stores
View
Actions
Stores
View
Actions
Dispatcher
Stores
View

Пример
"Банковский счёт"

Транзакция Значение Баланс
Создали счёт 0 0
Депозит 200 200
Снятие 50 150
Депозит 100 250

Action

{ type: Actions.WITHDREW_FROM_ACCOUNT, data: { accountID: 7, amount: 50, date: 1429468551933 } }

Store

let balance = 0; function onDispatch(action) { switch (action.type) { case Actions.WITHDREW_FROM_ACCOUNT: balance -= action.data.amount; break; } } getBalance();

В чём же разница?

- О! Так это же Модели. Сейчас я сюда Бекбончик прикручу.
- Я тебе прикручу! Ты где у Сторов Сеттеры увидел?

model.balance;
store.getBalance();

Object.observe(model, changes => {...});
store.subscribe(() => {...});

model.balance = 1000000;
???

Store - это функция от Actions

Store(initialState, ...actions) → currentState

Следующий шаг

Изменение счёта - это ведь ассинхронная операция!

function onDispatch(action) { switch (action.type) { case Actions.WITHDREW_FROM_ACCOUNT: requestWithdrawal( action.data.accountId, action.data.amount ).then(res => balance -= res.amount); break; } }
function requestWithdrawal(account, amount) { sendWithdrawalRequest(account, amount) .done( res => dispatch({ type: Actions.WITHDREW_FROM_ACCOUNT, data: {...} }), err => dispatch({ type: Actions.WITHDRAWAL_FAILED, data: {...} }); ); }

Но что если

Возникла ошибка

Но что если

Возникла ошибка
{ type: Actions.SHOW_NOTIFICATION, data: { message: "Возникла ошибка", ... } }

Но что если

Возникла ошибка
{ type: Actions.SHOW_NOTIFICATION, data: { message: "Возникла ошибка", ... } }

WRONG!

let messages = []; function onDispatch(action) { switch (action.type) { case Actions.WITHDRAWAL_FAILED: messages.push("Возникла ошибка"); break; case Actions.NOTIFICATION_DISMISSED: messages = []; break; ... } }

Что нам это даёт?

Мы абстрагируемся над конкретной реализацией и представлением данных.

Совокупность всех Action описывает состояние нашего приложения в данный момент времени.

Зарепортить баг проще простого!
Юзер прикрепляет историю всех Action с параметрами к багрепорту (ну не совсем юзер), а мы спокойно их прокручиваем в нужном порядке и получаем нужное состояние приложения

Общая схема

It's already Monday but we still don't have brand new Flux implementation

Who to follow...

Thanks!