ZarahioN Presents

Answering why

Laid-back

Отправка параметров и несколько одновременных запросов. React + Redux-saga. [Pt. 2]

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

Но это скучно.

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

function* fetchQuote(action) {
    try {
        const quote = yield call(requestYodified, action.text);
        const spellcheckedQuote = yield call(spellcheck, quote.data);

        yield put({type: success, quote: quote.data, spellcheckData: spellcheckedQuote.data});
    } catch (e) {
        yield put({type: fail, message: e.message});
    }
}

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

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

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

Ненадежен, Йода, ты же как…

Поэтому почему бы не расширить немного обработку ошибок — к примеру, чтобы совсем не разочаровывать пользователя сообщением о ошибке, мы проверим как у него с орфографией!

...
catch (e) {
    try {
        const spellcheckedQuote = yield call(spellcheck, action.text);
        yield put({
            type: spellchecked, results: spellcheckedQuote.data
        });
    }
    finally {
        yield put({type: fail, message: e.message});
    }
}

Таким образом мы можем, к примеру, вывести результат выполнения части запросов, если это более выгодно по сравнению с полной остановкой из-за ошибки где-то на промежуточном этапе работы. А за время работы над «неудачным» сценарием, Йода поднялся и приготовился к работе — можно отдать ему какой-нибудь текст для перевода в человеческий — к примеру, заголовок нашего приложения «Welcome to React sagas!», спустя несколько долгих секунд раздумий Йода наконец отвечает «To react sagas welcome! Herh herh herh.». Вот и замечательно.

    {
    type: 'QuoteFetchRequest@Front',
    text: 'Welcome to React sagas!'
    }

    {
    type: 'QuoteFetchSucceeded@Saga',
    quote: 'To react sagas welcome! Herh herh herh.',
    spellcheckData: {
        original: 'To react sagas welcome! Herh herh herh.',
        suggestion: 'To react sagas welcome Hersh her her.',
        corrections: {
        Herh: [
        'Hersh', ...
        ],
        herh: [
        'her.', ...
        ] } }
    }

С последовательными запросами мы разобрались — добавить еще десяток-другой в цепочку, чтобы заставить пользователя ждать минутами, дело техники и сокровенной комбинации ctrl+c -> ctrl+v. Осталось научиться делать одновременно много, как некогда почивший салат.

function* fetchQuoteWhilSpellchecking(action) {
    try {
        const [quote, spellcheckedText] = yield all([
            call(requestYodified, action.text),
            call(spellcheck, action.text)
        ]);

        yield put({type: success, quote: quote.data, spellcheckData: spellcheckedText.data});
    } catch (e) {
        yield put({type: fail, message: e.message});
    }
}

Как вновь видно — изменений очень много.

Функция all — очередной вспомогательный элемент saga, позволяет создать несколько запросов и, дождавшись выполнения каждого, продолжить выполнение саги с полученными значениями. Запись вида const [a, b, c] = [...] — альтернатива деструктуризации для массивов, ожидаемо переводит массив вида [3, 2, 4] в три переменные — a = 3, ...

Наяривание до десятка запросов опять же происходит старым, добрым и немого уставшим ctrl+c -> ctrl+v. Но помни, пользователь очень любит ждать и обладает наилучшим железом, чем больше количество и чем толще запросы выше качество запросов — тем лучше.

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

В следующем выпуске — Саги: интересные части. Эффекты, отложенное исполнение, комбинирование и предсказуемое ожидание действий.

Код опять же доступен в репо, помимо полного кода, там появился «индикатор» загрузки.

Эффекты и саги. React + Redux-saga. [Pt. 3]

Я должен признаться, я был не совсем откровенен, называя call или put вспомогательными функциями (и насчет еще некоторых моментов, но забудем пока про это).

call, put и многие другие являются фабриками Эффектов (Effect) — специальных объектов, напоминающих действия (action) из redux, только предназначенные для самой библиотеки saga.

Почему эффекты? Я бы сказал та же причина, что и у самого редакса — простота, плоскость и тестируемость — за счет мгновенного возвращения простого объекта эффекты позволяют тестировать только код генератора, без необходимости ожидать выполнения запросов или их подмены.

Как я уже упомянул, эффекты сильно напоминают собой действия:

{
  CALL: {
    fn: log,
    args: [42],
    context: null
  }
}

Так, к примеру, выглядит возврат из call (немного обрезанный, но тем не менее). Как видно «тип» эффекта прописан ключом объекта.

Впрочем к чему весь этот разговор — эффекты, вспомогательные функции или фабрики, какая разница?

Знакомьтесь, наша новая фабрика take:

function* typing(action) {
    try {
        yield put({type: typingStart});
        yield take(typingStopped);
        yield put({type: typingEnd});
    } catch (e) {
        yield put({type: "Welp, we did it"});
    }
}

После написания этого примера, я начинаю понимать, насколько

В любом случае, фабрика («вспомогательная функция» мне нравилось больше) take позволяет дождаться действия редакса изнутри обработчика. Применения? Бесконечны! О чем и говорит мой креативный пример.

Как? Мы же в мире генераторов. Исходный код я, конечно же, не смотрел, но предполагаю что saga перенаправляет вызов .next нашего обработчика-генератора на появление указанного действия и «БАМ!» совершенно магическим образом мы получаем это действие внутри обработчика как только оно появляется благодаря dispatch-у.

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

Однако, текущий вариант не слишком отличается от классического подхода и достаточно бесполезен в чистом виде, но это не конец. saga позволяет помимо прочего, использовать элементы «многопоточного» программирования — fork или race, помимо некоторых других.

Мы даже уже использовали all, который позволяет дождаться выполнения всех эффектов (обещаний) и получить все результаты в один массив. В saga есть его близкий родственник — race. Они похожи, только вот race ждет завершения любого из эффектов и возвращает объект с именованным результатом:

function* typing(action) {
try {
    while (true){
        yield take(textUpdated);
        yield put({type: typingStarted});
        while (true){
            const {updated, timeouted} = yield race({
                updated: take(textUpdated),
                timeouted: call(delay, 1000),
            })
            if (timeouted) {
                yield put({type: typingEnded});
                break;
            }
        }
    }
} catch (e) {
    yield put({type: "Welp, we did it"});
}
}

Как ни пугающе, но у нас теперь два бесконечных цикла (и скорее всего способ переписать это в более презентабельном виде).

Первый цикл нужен для прослушивания действия textUpdated. Как только мы его получили, можно вступить во второй цикл, перед которым мы отправляем в редакс сообщение, что пользователь начал печатать. Внутри мы создаем условие гонки — или пользователь продолжает печатать — updated— и мы ничего не делая перезапуская внутренний цикл, или проходит 1 секунда — delay(1000[ms]) — и мы отправляем сообщение, что пользователь перестал печатать и выходим во внешний цикл прослушивания.

Почему это лучше?

Несколько причин:

  1. Нам больше не нужно такое в компоненте:
start="1.">
clearTimeout(this.typingTimeout);
this.typingTimeout = setTimeout(() => typingStopped(), 1000);

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

  1. Код отвечающий за состояние печатания выглядит проще (за исключением двух циклов while(true) которые все же немного портят картину) и находится в одном единственном месте.
  2. Нам не нужна еще одна библиотека чтобы сделать задержку или отложенное действие. К примеру, если использовать all вместо race то в результате у нас получится массив со всеми собранными за секунду действиями — очень напоминает буфер из RxJS и подобных. Комбинируя несколько простых эффектов можно получить неплохой набор часто используемых инструментов.

start="2.">

Почему нет?

Я не уверен, есть ли у меня ответ. Избавление от таймера и вообще любых помех в теле компонента выглядит слишком привлекательно для меня на данный момент. Если у тебя найдутся возражения или предложения того, как это лучше реализовать, всегда готов выслушать.

На этом, я думаю, наш небольшой цикл уроков по redux-saga завершается. Осталось пару фабрик и вспомогательных функций, которые мы не разобрали, вроде fork, join или cancel, однако они имеют схожее поведение и очень похожи на свои аналоги из мира многопоточного программирования. Если нужда в их разборе появится, то мы несомненно этим займемся.

Опять же — код доступен в репоа документация по сагам на их сайте.

GraphQL + Apollo + Express = ? [Pt. 1 — Installation adventure]

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

Я как-то работал на проекте, у которого уже был базовый функционал и работал граф(кюэль? я продолжу называть GraphQL — граф а GraphiQL — графи, так проще, не спрашивай). Это был недолгий опыт, но именно тогда я по-настоящему влюбился в граф (и аполло) и все ждал возможности поиграть с ними еще немного.

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

Конечно же я помнил все необходимые библиотеки (express, apollo-server, duh) и быстро установив их задумался, «А как его вообще подключать?». По дороге я вспомнил, что мне слишком лень настраивать еще и клиента и будет неплохо подумать над будущей моделью имея интерактив графи без необходимости начинать корячить само приложение. Оказавшись в тупике я ушел в гугл и быстро вернулся со страничкой гитхаба, где было описано, как подключить графи (с самим аполло я же влегкую справлюсь, да?)

Первая проблема бросилась в глаза быстро — аполло ставится под конкретный фреймворк (surprise!) а чистый apollo-server скорее всего предназначен для чистого Node.js сервера либо кастомной настройки. Откатываем установку apollo-server, ставим apollo-server-express…

И вспоминаем что синтаксис запуска экспресса я с коленки тоже не помню… Ладно, гугл, сайт экспресса, ctrl+c, ctrl+v.

Так, экспресс поставили, графи подключили, кажется все?

Нет.. нужно запустить сам граф, чтобы было что проверять (surprise! x2). Процесс знаешь? Не угадал, страница гитхаба про аполло и графи лежит в репо самого аполло и.. прямо на главной странице для самых умелых они разместили полный шаблон для старта экспресс-сервера с аполло, графи и граф-ом (оригинал можно найти по ссылке). Моя полноценная копия:

[cc lang=»javascript»]
import express from ‘express’;
import bodyParser from ‘body-parser’;
import { graphqlExpress, graphiqlExpress } from ‘apollo-server-express’;

const app = express();

app.get(‘/’, (req, res) => res.send(‘Bye-bye World!’))

app.use(‘/graphql’, bodyParser.json(), graphqlExpress({ schema: myGraphQLSchema }));
app.get(‘/graphiql’, graphiqlExpress({ endpointURL: ‘/graphql’ }));

const port = 81;
app.listen(port, () => console.log(‘Example app listening on port ‘ + port))
[/cc]

yarn start — and fail.
Мораль сей басни? Мы (я) забыли схему, точнее просто отвлеклись на погоню за облаками.

Импортируем будущую схему и собираем ее через небольшую утилиту (или пишем ручками, кому как нравится):

[cc lang=»javascript»]
@index.js
..
import myGraphQLSchema from ‘./schema’;

@schema.js
import { makeExecutableSchema } from ‘graphql-tools’;

import typeDefs from ‘./types’;
import resolvers from ‘./resolvers’;

const schema = makeExecutableSchema({
typeDefs,
resolvers,
});

export default schema;

@types.js
const Recipe = `
type Recipe {
id: Int!
message: String
author: String
}
`;

const Query = `
type Query {
recipies: [Recipe]
}
`

export default [Query, Recipe];

@resolvers.js
export default {
Query: {
recipies: () => [’empty’,’empty’,’empty’,]
}
}

[/cc]

Все? Все, не учитывая ленивый и пустой обработчик (resolver), но это на будущее.

Что мы сегодня узнали? Дети, используйте готовые шаблоны или придется писать технически бесполезные блог-посты. Ситуация не так уж и пичальна, впрочем — экспресс запущен, аполло «настроен» и все практически работает.

Ссылки на документацию для самых интересующихся (и тех, кто использует koa, hapi, restify, lambda, micro, azure-functions, adonis и хочет особенного отношения):
Apollo main doc
Apollo GraphiQL
How to Apollo GraphQL Schema
Express blank (Bye World) app

Первые серьезные строки на React.js [Pt.3 – Finally starting for real]

В прошлых монотонных рассказах мы занимались долгой и утомительной настройкой вебпака и вообще «среды» для создания React приложений.

К счастью, это мучение наконец закончилось и мы можем приступить к созданию нашего приложения. Ура, свобода!

Впрочем, начнем с азов, затронутых еще в первом рассказе. Заглянем в файл index.js, он скорее всего находится в папке src, если ты продолжаешь с пути c-r-a, либо прямо в корне проекта, если решил в лоб принять удар тяжелой и героической самостоятельной настройки вебпака.

Мы завершили наше прошлое знакомство с кодом реакта на небольшом примере двух вариантов компонентов, умном и глупом, как я, пожалуй, буду дальше их называть. Первый по моей скромной (и скорее всего не очень надежной) терминологии имеет свою не визуальную логику, второй просто отображает разметку подставляя переданные параметры.
Как я уже говорил, существует еще как минимум пара типов и принципов разделения компонентов, но это разделение несет с собой несколько тонких граней, которые с наскока легко проморгать.

Для простоты подхода (и соблюдения традиций) мы начнем с глупых — stateless — компонентов.
На данный момент существует 3 способа их создания, впрочем последний (и исторически первый) через React.createClass мы не будем разбирать как технически устаревающий (и не зря же мы проходили через все страдания вебпака и бейбла)

class X extends React.Component

import React, { Component } from 'react'; 
import ReactDOM from 'react-dom'; 

class ColoredDiv extends React.Component {
    render() {
        const {color, fontSize: fz} = this.props;
        return <div style={{background: this.props.bgColor, color, fontSize: fz}}>Me colored, yay!</div>
    }
}

ReactDOM.render(<ColoredDiv bgColor="green" color="red" fontSize="14px"/>, document.getElementById('root'));

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

Разберем же наш простенький код по полочкам:
import X from 'x'; — механизм загрузки и сборки модулей, для которого мы и используем вебпак, так как это до сих пор достаточно далекий от браузерного стандарта функционал. Как можно правильно догадаться, import React from 'react' дает доступ к функционалу реакта посредством константы React (также любой импорт говорит вебпаку загрузить указанную библиотеку, что бывает важно в некоторых случаях).
class X extends React.Component — синтаксис для создания «класса» в ES6, при этом мы наследуем от базового класса реакта (потому что надо).
render() это метод нашего класса, который будет использовать реакт для создания разметки компонента — ее рендера.

this.props это специальный неизменяемый объект (свойство\параметр), в который реакт помещает переданные компоненту свойства (prop(ertie)s).

Непосредственно процесс передачи можно увидеть глянув на последнюю строчку — ReactDOM.render. Первым аргументом для него идет JSX разметка (технически возврат React.createElement, но это ненужные детали). Мы использовали имя созданного компонента (класса) и вставили несколько атрибутов: bgColor, color, fontSize эти атрибуты и попадают в объект props компонента.

Как можно заметить, в рендере самого компонента мы возвращаем JSX разметку подобную той, что передавали в ReactDOM.render (наблюдаешь связь?). И как можно заметить, фигурные скобки в атрибутах служат для передачи JS значений, включая объекты как в style (отчего там получились по две фигурные скобки, внешние для обозначения компилятору что идет JS код, внутренние уже для самого объекта), так и просто строки или числа.

Также я использовал сразу три способа использования переменных из свойств (props): доступ напрямую this.props.bgColor, и деструктуризации {color} = this.props и {fontSize: fz} = this.props, третья отличается от второй лишь тем, что мы переименовали переменную перед использованием. Как правило используется второй и третий тип, так как это позволяет сразу в начале рендера указать, какие свойства используются в компоненте.

Как можно догадаться, фигурные скобки позволяют использовать JS не только в атрибутах:

render() {
    const {color, fontSize: fz } = this.props;
    return (<div style={{background: this.props.bgColor, color, fontSize: fz}}>
        {Math.random() >= 0.5 ? 'Me colored, yay!' : 'Me no random!'}
    </div>)
}

Помимо скромных забав со случайными числами таким образом можно использовать любой JS код, который возвращает значения (строки, числа, элементы реакта (JSX) или массивы перечисленных типов), к повсеместно используемому примеру, Array.map:

class ColoredDiv extends React.Component{
    render() {
        const {color, fontSize: fz, rows } = this.props;
        return (<div style={{background: this.props.bgColor, color, fontSize: fz}}>
            {rows.map(row => <li>{row}</li>)}
        </div>);
    }
}

ReactDOM.render(<ColoredDiv 
    color="#ab00ff" 
    bgColor="white" 
    fontSize="14px" 
    rows={[
        'Me colored, yay!', 
        'Me colored, nope second try!', 
        'No random is it!'
    ]}
/>, document.getElementById('root'));

Как можно ожидать (или нет, с непривычки) — ColoredDiv выведет уже знакомый стилизованный div и li со строками из массива переданного через свойство rows (ReactDOM.render).
Если ты еще не ознакомился с функциональными методами массивов, постарайся разобраться хотя бы с минимальным набором map, filter, reduce, и опционально forEach, последний технически идентичен map, просто не возвращает значений. Но особенно непредсказуемо полезным может оказаться reduce. А первые два ты скорее всего будешь использовать повсеместно, работая с реактом (и очень вероятно без него тоже).

Помимо бездумного разглядывания кода, я предлагаю тебе самостоятельно побаловаться с нашим скромным примером, использовав все три перечисленных типа данных доступных для вывода, и в целом привыкнуть к подобию JSX на HTML и его отличительным особенностям, которые позволяют творить (иногда к сожалению) что угодно за счет вставки чистого JS кода в разметку. Особенно интересно становится, когда ты заканчиваешь играть со стандартными HTML елементами и начинаешь компоновать разметку компонентами реакта.

Итак, что мы успели сделать:
1. Создали класс-компонент ColoredDiv через class ColoredDiv extends React.Component { ... },
2. Добавили в него метод render() { ... } который возвращает JSX разметку (<div style={{ ... }}>...</div>),
3. Отрендерили (или правильнее сказать монтировали (mount)) созданный компонент посредством ReactDOM.render(<ColoredDiv ... />, realDOMElem),
4. Передали свойства (props) компоненту ColoredDiv через ReactDOM.render и использовали их.

Базовое знакомство с работой реакта? Завершено.

Хоть это и скучно, но на сейчас почти хватит, осталось только глянуть второй способ записи глупых компонентов, о котором я говорил ранее:

const X = (props) => …

Как можно заметить, имея опыт с ES6, это просто «стрелочная» (arrow) функция. Такой тип записи компонентов называется функциональным (duh!) и является более простой и удобной заменой относительно громоздкому классу. К слову, использовать стандартные функции теоретически тоже можно, но лично я такое не практикую.

Также я упоминал, что наш чрезмерно неумный компонент можно без дополнительных манипуляций преобразовать в функциональный:

const ColoredDiv = ({color, fontSize: fz, rows, ...props }) => 
<div style={{background: props.bgColor, color, fontSize: fz}}>
    {rows.map(row => <li>{row}</li>)}
</div>

Как всегда немного магии деструктуризации и условно громоздкая конструкция класса «сокращается» в два раза. Условно и кавычки подсказывают, что все не так просто. Куда большую пользу функциональные компоненты несут своей семантикой — они по определению не имеют своего состояния и поэтому они должны быть просты..
но.. никто не помешает сделать «умный» компонент-обертку который передает все данные и обработчики «глупому» функциональному компоненту.. который тем не менее содержит сотню строк разметки перемешанной с данными и обработчиками. И технически будет прав, но работать с этим все равно будет не очень приятно.
Что я хочу сказать — нельзя прятаться за терминами и определениями, оправдывая не очень хороший код (duh!x2), нужно стараться писать хороший код, помогая себе определенными терминами и абстракциями или шаблонами скрывающимися за ними.

На этой недовольной лирической ноте мы завершаем знакомство с практическими азами реакта.. и начинаем наш для некоторых короткий, а для некоторых очень долгий путь познания современных реалий веб-разработки с React.js.

Почему вообще Реакт? [Pt.4 – Why bother]

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

Я как-то наскоком погнал ставить вебпак, ваять реакт и даже не поудосожился предложить один очень интересный вопрос: а зачем он вообще?

Для ответа на него, думаю, стоит вернуться к «реальному программированию» на ПК — «прикладному программированию оконных приложений» (desktop applications), это технически все пользовательские программы на Windows и macOS. С Linux все немного интереснее, но даже там «оболочку» приобретают все больше приложений с ростом базы обычных пользователей.

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

Однако немножко я все же знаю и попробую доходчиво пояснить.

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

Поэтому со временем приложения все больше переходили от текстового представления или «интерфейса» в виде консоли (опционально расширенной на весь экран) отображающей только текст, к тому что ты (скорее всего) обычно видишь у себя на компьютере — приложения с кнопками, картинками, анимациями и прочими прелестями старательной работы дизайнеров. К чему я это? С увеличивающимся количеством и разнообразием контролирующих элементов росла и сложность стоящего за ними кода — со временем появилась нужда в правилах управления этим всё расширяющимся хаосом.

И тут на сцену выходит MVC — Model-View-Controller, ставший классикой и бичом начинающих познавать мир подход, архитектура и шаблон создания (относительно) сложных приложений. Как всегда, почитай о нем на досуге, только осторожно, его постигли излишняя популярность и возраст, и это породило кучу мнений, рассуждений и взглядов на то, каким должен быть «правильный» MVC.

В моих личных и скромных мнении, рассуждении и взгляде (duh) он представляет собой.. ужасный пример общепринятой нормы, или же прекрасный пример того, как испортить Что-то хорошее кучей рассказывающих и спорящих про Это голосов.

В своей базовой форме (из-под очередного коверкающего неполноценным пониманием взгляда) он представляет собой простую структуру разделяющую приложение на несколько отдельных и максимально независимых слоев — Модель, Вид (представление) и Контроллер (логика?). Модель представляет собой данные приложения, Вид — то, как эти данные представлены пользователю, Контроллер — то, каким образом пользователь через Вид может влиять на Модель (?). Все вроде просто, но есть одна ма-а-аленькая проблема — мое представление о MVC вполне вероятно ошибочно и вполне вероятно не совпадает с представлением Васи сидящего на другом конце интернета.

Все становится еще немного интереснее, когда ты узнаешь, что помимо MVC есть практически, но сильно не совсем, такие же подходы вроде MVP или MVVM, и у каждого может быть по несколько разных вариаций.
Надеюсь я достаточно тебя запугал и запутал.

Flux на спасение (глуповатых мартышек)!

Удивительно, но не я один страдал от неочевидности и можно сказать искусственной сложности привычного подхода, особенно того хаоса, что часто царил в веб-разработке. Поэтому компания добра ™ Facebook представила (и скорее всего втихоря у себя использовала некоторое время) Flux-архитектуру. Из своего скромного опыта я бы сказал, что она похожа на доработанный и функциональный MVVM, но не буду, так как они все достаточно схожи.

Что же из себя представляет флакс (флукс? флякс? фляжка.. Flux!)? Это однонаправленная архитектура построения приложений, включающая Представление (Вид, View), Состояние (Модель, Store) и связку Действий и Диспетчера (Action+Dispatcher, не совсем, но в некотором роде Контроллер).
В отличии от MVC, где, в некоторых трактовках и подходах, Представление могло общаться как с Моделью так и с Контроллером, иногда еще и напрямую обновляя первую за счет действий пользователя, во Flux Представление получается за счет декларативного преобразования Состояния, Состояние же образуется за счет изменений созданных Диспетчером, которые в свою очередь вызывают Действия, которые могут быть созданы Представлением (пользователем) либо самой системой.

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

]

Должен признаться, я скорее всего был плохим программистом, когда только столкнулся с необходимостью создавать интерфейсы на C#, я частенько путался и не был уверен, что, куда, зачем и почему. Не то чтобы я стал сильно лучше сейчас (я бы назвал себя смышленой мартышкой, пожалуй) но модель флукса я понял и принял мгновенно. И.. думаю это именно то, чего мне всегда так не хватало при попытке создать прикладное приложение (и декларативного реакта в виде представления, но это уже очень приятные мелочи).

Вспоминая о Реакте, технически он может представлять собой всю систему флукса: очевидно в основе он Представление, однако помимо этого есть Состояние (умные statefull компоненты), возможность его менять — Диспетчер (функционал по обновлению Состояния) — и в конце-концов Действия, которыми можно представить простые объекты переданные в Диспетчер. Однако, это хоть и удобный порой способ, при практически любой сколь немного сложной компоновке (или не дай Ишвар работы в команде), он быстро перестает быть хоть сколь эффективным.
Тем не менее, скоро мы его разберем, как базис для пробы пород флукса и поиск проблем, почему реакт сам по себе таки не выход.

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

Small notification

Если ты немного интересовался нашей (ZN Group) работой, то, возможно, заметил наши скромные благотворительные потуги, которые на данный момент освящаются на сайте издательства.

К чему оповещение? Все просто — мы одни никогда не сможем сделать достаточно, сколько бы не старались и пытались, поэтому я предлагаю тебе заглянуть и поинтересоваться о благотворительности, которую руководство решило поддержать в этот раз. Если понравится — пожертвуй или расскажи о ней тем, кто в этом заинтересован.

На сим, пожалуй, все. See ya!

Read More