Комментарии 78
Линтер можно добавить как плагин к тому же Саблайму, например. Это ускорит процесс разработки в том плане, что не придётся исправлять то, на что будет ругаться eslint после сохранения всех изменений. Хотя, с другой стороны, если самому определять правила в .eslint.rc, то он, линтер то бишь, может и не понадобиться.
Верно. Различные lint плагины есть к Atom, WebStorm, TextMate. Для других популярных IDE и редакторов тоже должны быть плагины, но у меня пока есть опыт работы с этой тройкой.

Каждый плагин умеет что-то свое, но еще нет такого, который бы исправлял прямо все проблемы кода разом, да это и невозможно. Насколько мне известно, команда eslint сейчас работает над инструментами, которые бы частично решали эту проблему.
Во второй части будет немного CSS, но в упрощенном виде. Дело в том, что в React-сообществе до сих пор нет единого «правильного» подхода к стилям.

Один из core-разработчик, работая над react-native решил использовать inline-стили, чтобы не возиться с написанием css-парсера на тот момент. Неожиданно этот «анти-паттерн» оказался достаточно популярным среди сообщества и сейчас эта идея получила широкое развитие.

Мне нравится идея, что стили компонента лежат рядом с ним, а не определяются глобально. Другое дело, что inline-стили не кешируются и работают медленнее, чем классы.

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

Так как статья рассчитана в основном на новичков, то мне показалось, что лучше на этом этапе тему css опустить и написать потом отдельную статью, если будет много интереса к этой теме.
А вам удалось подружить jss с ExtractTextPlugin? Это вообще возможно?

Да, и оно как-то очень естественно вышло. Может мне повезло и я случайно обошел грабли :)

Можно кусок webpack-конфига на эту тему, пожалуйста? Очень лень самому всё раскапывать :)

Конфиг из статьи. Я до сих пор суть проблемы не понимаю. То, что вы определяете в jss-стилях, не экстрактится by design. То есть у вас:
1) один большой css, который сформирован на базе классических импортов
2) jss все свои стили при Server Side Rendering формирует и вставляет в HTML, если не используете SSR, то просто по одному блоку style на каждый тип компонента, который ренденрится на странице

Ну вы так быстро ответили "да", видимо не поняли вопроса. Я даже удивился :) Конфиг из статьи к jss не имеет никакого отношения.


То, что by design — это понятно, но помечтать о том, чтобы jss выдрать build-time, вполне можно, если не использовать его динамические фичи. Вот, например, попытка (насколько я понял, заброшенная) сделать что-то на эту тему: https://github.com/markdalgleish/jss-loader

Мм, тогда зачем вам jss? Вы же хотите избавиться от фишки, которая лежит в его фундаменте. Можно сделать форк, взять build-time, выкинуть все ненужное и получить какой-нить новый *ss :)

Строки build и nodemon в package.json должны выглядеть так:
«set NODE_ENV='production' node node_modules/webpack/bin/webpack -p»
«set NODE_PATH=./src node node_modules/nodemon/bin/nodemon server.js»


А в файле .eslintrc замените строку «linebreak-style»: [2, «windows»]
P.S. Извиняюсь за двойной комментарий, мой косяк.-

все же
set NODE_ENV='production' && node node_modules/webpack/bin/webpack -p

А второй кроссплатформенно заменяется на
node node_modules/nodemon/bin/nodemon server.js ./src


Ну и от себя, webpack и nodemon будут нужны не в одном проекте и имеет смысл их ставить глобально.
Тогда еще проще
set NODE_ENV='production' && webpack -p
nodemon server.js ./src

В а чем преимущество глобальной установки, кроме экономии на символах?
Во-первых, на linux для глобальной установки нужно sudo
Во-вторых, не всегда все проекты используют одну и ту же версию библиотеки. Особенно это актуально при скором релизе webpack 2.

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

Кажется экономия на паре пакетов в npm install все равно не стоит того, что придется потратить на разборки, почему ничего не собирается, если пытаться запустить webpack 1 на проекте в webpack 2. Явная декларация версии webpack и всех остальных иструментов тоже экономит время.


Но это мое мнение из собственного опыта.

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


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


А остальное — это наживное и с опытом придет.

Я лично привык сразу все ставить глобально (на локальной машине), потом просто используешь инструменты даже не задумываясь об установке.
Возможно кому то поможет. У меня nodemon, смог правильно подключить server.js только после того как в конце NODE_PATH была добавлена точка с запятой, то есть получилось так:
set NODE_PATH=./src; && nodemon server.js
А добавьте, пожалуйста, к Hello World авторизацию с JWT, компонент авторизации в React.js и проверку на валидность токена. А то TODO-tutorials много, а толковых примеров нет.

Я бы рад, но в моем основном проекте я начал использовать devise_token_auth на бэкенде, соответственно на стороне front-end — redux-auth. Спустя некоторое время я его переписал и опубликовал в виде redux-oauth, так как redux-auth не поддерживал ряд важных мне фич (например, API requests для server-side rendering), плюс он непомерно тяжелый для того, что умеет. Я планировал в своем цикле осветить именно этот стек, так как он работает у меня в продакшене примерно полгода без видимых проблем, и я им более, чем доволен.


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

Для каких сайтов / бюджетов оправдано городит огород из React.js?
Много разработчиков его понимают или только избранные? :)

Этот механизм позволяет пользователю получить доступ к контенту существенно быстрее.

Каким образом?

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

Что входит в эти 2 секунды?
Это время генерации страницы?

Выигрыш получается за счет того, что не надо дожидаться скачивания клиентского JavaScript, а это 200кб и более с учетом минификации и сжатия.

Почему же не нужно?

изоморфный подход делает ваше приложение гораздо приятнее для пользователя.

В чем приятность, если отвалились скрипты?

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

Почему?
  1. То, что описано в статье разработчик с опытом собирает в течение 5-10 минут. Написание самого приложения по скорости мало отличается от других технологий. Facebook, AirBnB, Twitter, порталы Yahoo, девпортал Apple и масса других сайтов — это все сейчас на реакте
  2. В 2 секунды входит загрузка JS и инициализация, причем если временем загрузки можно пренебречь, то вот с инициализацией вы ничего сделать не сможете. Если у вас client-side rendering, то вот эти 2+ секунды JS будет запускаться и формировать страницу, а пользователь — любоваться на заставку "Loading".
  3. Не нужно ждать, потому что сервер Node.js отдает HTML с контентом. Да, в течение этих 2 секунд кнопочки работать не будут, но скорее всего пользователь и не успеет ничего сделать
  4. Если отвалились скрипты, то client-side приложение даже ничего не покажет, изоморфное же в теории работать дальше, как nojs приложения

Я Вас понимаю, я много лет относился к node и серверному JavaScript с огромным скепсисом и до сих пор считаю, что писать backend на ноде не надо вот совсем, но после того, как сам попробовал реакт на одном из своих проектов и генерация view слоя упала с 20 секунд до 0.5 при переходе с rails на rails-api + node.js, выяснилось, что за последние несколько лет очень многое изменилось в индустрии и что производительность JavaScript может доходить до 30% от C, а не 1%, как когда-то было.


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

Не нужно ждать, потому что сервер Node.js отдает HTML с контентом. Да, в течение этих 2 секунд кнопочки работать не будут, но скорее всего пользователь и не успеет ничего сделать

А как обрабатывается случай, когда всё таки успеет?

И насколько сложнее сделать изоморфное приложение с бэкэндом не на Node? То есть понятно, что теоретически можно выполнить JS-код на чём угодно, но есть ли для этого готовые инструменты хотя бы в мейнстримных Java, C#, Python, etc.?
  1. It depends. Если у Вас кнопка, которая чистый JavaScript (ну там выбор цвета аватарки в виджете, который генерит аватарки), то упс. Если это ссылка или кнопка формы, то вместо того, чтобы логика обрабатывалась клиентским JS, который вероятно сделает что-нибудь полезное (отрендерит часть страницы и покажет loading indicator, например, или сделает валидацию), будет выполнено "классическое вебовское действие", то есть запрос уйдет на сервер и форма будет отправлена POST'ом соответственно. Важно понимать, что эти fallbacks не совсем бесплатны с точки зрения работы программиста и нужно их реализовывать там, где надо, а не везде. Но это здорово, что есть такая возможность, и в теории можно сделать даже так, что сайт вполне себе работал даже если у клиента JS отключен совсем.


  2. Я очень часто сталкиваюсь с таким вопросом, когда рассказываю знакомым про изоморфные приложения. В примере и в следующих частях я пишу как раз такое приложение: фронт изоморфный, а бек — на rails-api.

То есть у Вашего веб-приложения будет 2+ серверной части: backend, который предоставляет REST API и node.js часть, которая рендерит HTML.


Грубо говоря, раньше у Вас было rails или там php, C#, Python, whatever приложение, которое а) обрабатывало запросы б) рендерило HTML, который отдавало клиенту.


Теперь у Вас разделились сущности и это, на самом деле, очень здорово: а) приложение на whatever технологии обрабатывает запросы и отдает JSON ответ и б) приложение на node.js, которое обрабатывает запросы, делает запросы к API при необходимости и отдает HTML клиенту. И вот б) в этой схеме нам нужен, чтобы сайт работал быстрее с точки зрения пользователя, отдавал поисковикам контент без необходимости выполнять JS и "сглаживал" ошибки клиентского JS. Тут важно отметить, что разницы между клиентским и изоморфном подходом с точки зрения кода очень мало: код клиентской и серверной части совпадает на 95%+, то есть реализация этой фичи обходится очень дешево, принимая во внимание, сколько пользы она приносит


Тут еще добавлю, что разделять front и back очевидно хорошо, потому что


  • тот же API может быть использован мобильными приложениями
  • удобно разделить разработку на две команды
  • проще тестировать
Не совсем понял соответствие пунктов ответов вопросам… :)

1. То-то я вижу, они тупят :)
2. Хм, то есть по факту не на 2с быстрее, а скорее всего на 2с медленнее :) В классике js инициализируется моментально после загрузки.
3. А PHP не отдает что ли? :) Ни чем не лучше.
4. Разницы нету…

5. Я не то что отношусь со скепсисом.
NodeJS на самом деле очень быстрый, но не так просто переносить на асинхронную модель синхронный код.
Может есть наработки это упрощающие, хз.
6. Что это за приложение с генерацией 20 с? Это на сервере столько шаблон отрабатывал? Да, Руби тот еще скороход :)
7. Зачем мне погружаться, если я слышу только голословные утверждения?
А PHP не отдает что ли? :) Ни чем не лучше.

Вы или не понимаете, о чем говорите, или это тролллинг.
PHP отдает статичный html, который так и останется статичным html, можно лишь вручную его менять. React на сервере отдает статичный html, который на клиенте сразу же подхватывается клиентским реактом и дальше работает так, будто этот реакт его и сгенерировал (то есть при обновлении состояния будет обновится и html).
Я сам уже не помню, что вкладывал в то, что Вы процитировали. :)
Возможно я что-то недопонял. :)

Спасибо, рад что понравилось. Я планирую опубликовать в середине следующей недели вторую часть и еще одной неделей позже — третью

У меня в разделе 5 после команды
npm run nodemon
выдало ошибку, что Express не найден. Добавьте, пожалуйста, команду
npm install express
чтобы обозначить установку фрэймворка
Ремарка по поводу сервера, его тоже надо собирати, иначе при первом запросе будет просидание по скорости. я так понимаю из-за babel-loader.
Вот пример зборки на сервере
var webpack = require('webpack');
var path = require('path');
var fs = require('fs');

var nodeModules = {};
fs.readdirSync('node_modules')
    .filter(function(x) {
        return ['.bin'].indexOf(x) === -1;
    })
    .forEach(function(mod) {
        nodeModules[mod] = 'commonjs ' + mod;
    });

var babelPlugins = ['transform-runtime'];

module.exports = {
    entry: './index.js',
    target: 'node',
    output: {
        path: path.join(__dirname, 'build'),
        filename: 'backend.js'
    },
    module      : {
        loaders: [
            {
                loader : 'babel',
                exclude: /node_modules/,
                query: {
                    plugins: babelPlugins,
                    presets: ["stage-0", "react","es2015-node5"],
                }
            },
            {
                test: /\.css$/, loader: "style-loader!css-loader"
            },
        ]
    },
    externals: nodeModules,
    plugins: [
        new webpack.IgnorePlugin(/\.(css|less)$/),
        new webpack.BannerPlugin('require("source-map-support").install();',
            { raw: true, entryOnly: false })
    ],
    devtool: 'sourcemap'
}

не забудте забрать полифил и babel-register с серверного кода

Спасибо за дополнение. Я не включил скрипт для сборки серверной части, так как не планировал рассматривать процесс деплоя в этом цикле, тем не менее, считаю, что Ваш комментарий может оказаться читателям весьма полезным

Отличная статья! Хочу задать наверное глупый вопрос. Почему в классе App handleNameChange не обычный метод, а class property?

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


Спасибо

Если сюда добавить необходимость делать клиентские API запросы после инициализации и вспомнить, что на мобильном интернете часто можно столкнуться с весьма ощутимыми задержками

Это вы скорее описываете преимущества серверного рендеринга, а не изоморфного. Ведь во втором случае это будет происходить только при загрузке первой страницы. Если js успешно подгрузится, конечно. А дальше всё те же запросы к API.

Я бы добавил ещё поисковую оптимизацию в плюсы. Гугл индексирует js долго и не всегда правильно, яндекс — не индексирует совсем.

Правильно ли я понимаю, что babel-polyfill просто ищет упоминания всяких Promise в коде и добавляет полифилы в бандл? То есть даже если браузер современный ему всё равно всё это придётся скачать? Или возможно создавать разные бандлы под современные и старые браузеры?

https://babeljs.io/docs/usage/polyfill/


В принципе да, примерно это он и делает. Я не экспериментировал, поэтому пока не имею такого опыта, но технически не вижу проблем сделать 2 сборки: с полифиллом и без. Соответственно express по User-Agent понимает, кто к нему пришел и отдает HTML шаблон с соответствующим бандлом.

А почему вы решили описывать настройки в .babelrc, а не в конфиге вебпака?

reuse же. Babel запускается и для node, и для webpack, и для тестов, и для много чего еще. Все они делят общий .babelrc конфиг

Для npm run скриптов можно не указывать node_modules/...., соответственно и webpack глобально не ставить:
In addition to the shell's pre-existing PATH, npm run adds node_modules/.bin to the PATH provided to scripts. Any binaries provided by locally-installed dependencies can be used without the node_modules/.bin prefix.

Спасибо за дополнение, не знал, а руки до прочтения спецификации еще не дошли

После 6 лет на Ruby (но почти без рельсов, чистые sinatra, grape и тд) попытка осознать webpack.config.js немного взрывает мозги

Спасибо за труд. Штудируя статью документации по оптимизации производительности, не втыкаюсь, почему этот код не работает:


  handleClick() {
    // This section is bad style and causes a bug
    const words = this.state.words;
    words.push('marklar');
    this.setState({words: words});
  }

а этот работает:


  handleClick() {
    this.setState(prevState => ({
      words: prevState.words.concat(['marklar'])
    }));
  }

Спасите-помогите! :)

Я не уверен конечно, но после setState состояние вроде как не изменится (потому что push уже по ссылке меняет данные в старом state), а значит и перерисовки не будет.

Так здесь вроде всё, как и должно быть. state явно задается в конструкторе, а потом уже меняется только через setState.

1) Вы инициализируете state в конструкторе, все работает, противоречий нет
2) Вы в toggleState меняете state через setState. setState — это не просто сеттер JS для аттрибута state, это функция, которая делает много всего. Противоречий нет
3) В render, вы обращается к state напрямую но на чтение, это никто не запрещает. Противоречий нет.


Биндить метод в render — плохая практика. Это не критично — оно будет работать, просто создаете оверхед на ровном месте, которого легко избежать.


В конструкторе пишите
this.toggleState = this.toggleState.bind(this);


в render — onClick={this.toggleState}


Подробнее: https://ryanfunduk.com/articles/never-bind-in-render/

Проблема другая. Но я не понимаю. Цитирую из статьи:


The problem is that PureComponent will do a simple comparison between the old and new values of this.props.words. Since this code mutates the words array in the handleClick method of WordAdder, the old and new values of this.props.words will compare as equal, even though the actual words in the array have changed. The ListOfWords will thus not update even though it has new words that shoud be rendered.

Действительно, вот так работает:


  handleClick() {
    this.setState({words: this.state.words.concat(['marklar'])});
  }

Но тогда зачем нужна вот эта вариация с prevState?


  handleClick() {
    this.setState(prevState => ({
      words: prevState.words.concat(['marklar'])
    }));
  }

Ох, я понял! Эквивалентная запись с использованием spread:


  handleClick() {
    this.setState({...this.state, words: this.state.words.concat(['marklar'])});
  }

Когда вы рендерите список элементов, вам надо передать каждому из них prop "key", который должен быть уникальным и быть как-то связанным с элементом (передавать значения от 1 до i является антипаттерном, так как не несет никакого смысла с точки зрения фреймворка). Если мы вставляем (или удаляем) элемент из списка, то при ререндеринге мы более эффктивно будем реюзать существующие элементы.


State A1:


<li key="11">11</li>
<li key="22">22</li>
<li key="33">33</li>
<li key="44">44</li>

State A2:


<li key="1">11</li>
<li key="2">22</li>
<li key="3">33</li>
<li key="4">44</li>

State B1:


<li key="11">11</li>
<li key="22">22</li>
<li key="44">44</li>

State B2:


<li key="1">11</li>
<li key="2">33</li>
<li key="3">44</li>

Если упрощенно, то при переходе от A1->B1 у нас будет 1 обновление DOM, а из A2->B2 — 3, хотя с точки зрения юзера UI идентичен

Объясните, пожалуйста, зачем нужен webpack-dev-server? Ещё вторым окном его запускать и держать в runtime?

В моём проекте перед запуском сервера webpack собирает bundle.js и всё. Для чего нужен какой-то webpack-dev-server? Для чего это ненужное усложнение?

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

В статье описано, зачем нужен webpack-dev-server. Приведи конкретную цитату из статьи, которая тебе не понятна

Каждый раз пересобирать весь проект может быть весьма накладно: для проекта среднего размера сборка легко может достигать 30 и более секунд. Чтобы решить эту проблему, во время разработки очень удобно использовать webpack-dev-server.

С чего взято, что время сборки занимает 30 секунд? Занимает 2-3 секунды. Если поставить watch: true, то bundle.js сам пересобирается. А причём тут webpack-dev-server? Вот это и непонятно. Я хочу разобраться. Пока не разберусь, не успокоюсь.

webpack — это сборщик, он конвертирует проект в конечные ассеты: js и css
webpack-dev-server — это сервер, который запускает webpack для первоначальной сборки и далее для каждого измененного файла. Его задача: сборка + хостинг ассетов в процессе разработок + hot reload, то есть обновление страницы в браузере автоматически в процессе разработки


С чего взято, что время сборки занимает 30 секунд?

речь же не о хелло вордах

Спасибо, разобрался в этом всём. Можно спросить вот ещё о чём…

Нельзя ли прямо из react-приложения обратиться к БД? Вроде как нельзя, так как это javascript. А как тогда сервер в SSR-приложении рендерит готовый html с выгруженными из БД данными? Куда ни смотрю, везде для выгрузки данных делают ajax-запрос. А как это происходит в случае SSR. Какая-то путаница в голове.

То есть, чтобы сренденрить страницу, сервер проделывает http-запроосы (а то реально долго в сравнении с прямым запросом к БД)?

SSR делает такой же запрос к API как и приложение в браузере. Если ты один раз сделаешь честное профилирование, то увидишь, что разница между http и прямым запросом к БД будет не очень большой, но взамен:


1) ты не привязываешься к БД: сервис предоставляет интерфейс, а откуда он берет данные неважно (сейчас БД, потом nosql, потом что-нибудь еще)
2) ты не пишешь два раза код, который делает одно и тоже (SSR дергает бд, frontend — API)
3) frontend никогда не должен лезть в БД напрямую в боевом проекте. У тебя просто заберут весь контент и все

То есть вообще никак не выгрузить данные из БД в срендеренный HTML на сервере до отправки ответа клиенту? Для чего мне это надо? Для поисковых систем. Когда робот лезет на страницу site.com/news, в html-коде страницы он должен видеть текст новостей! А этого текста нет, так как он загрузится потом, сделав ajax-запрос через некоторое время. То есть по сути это никакой не server-side rendering. Он server-side rendering только, чтобы срендерить пустые компоненты без данных. А это реально тупо. Я просто возмущен, что так происходит.

Спасибо за ваши ответы. Хотелось бы узнать от опытного разработчика, как вы преодолели эти вещи.

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


Во время SSR сервер рекурсивно обходит компоненты (для этого надо какую-нибудь либу взять или самому этот механизм реализовать) и собирает какие запросы к API нужно выполнить, чтобы отдать страницу со всеми данными.


После того, как список запросов известен, сервер их выполняет, их результат сохраняется в redux store и только после этого выполняется рендеринг с использованием всех нужных данных из стора. В результате получается HTML страница со всеми данными. Это страница + состояние redux store отдается клиенту.


Клиент отображает HTML и в фоне загружает JS, инициализирует Реакт приложение и загружает в redux начальное состояние, которое передал сервер.


Соответственно, SE и другие, лишенные JS, просто получают HTML с данными и до JS шага не доходят, что в принципе ок.

А вообще удивительно. В статье привели пример, который никогда не используется… А сейчас ответ в духе — нарисуйте два круга, а потом сову… Придётся искать ответы в другом месте. Думаю, не я один сталкиваюсь с этим. При этом в документациях этого ничего не описано. Жаль.
Наконец-то таки решил свою проблему с отображением данных из API в ответе от сервера. Дня четыре сидел над этим. Дело в том, что эту хитрую вещь никто не описывает. Я пересмотрел кучу турториалов, статей, выступлений… И все как один рендерят только пустые компоненты, без данных из API.

https://blog.tableflip.io/server-side-rendering-with-react-and-redux/ — в этой статье всё хорошо написано. Может, кто-то, как и я, тоже столкнётся с этой проблемой. Поэтому эта статья им поможет.
Отличие в транспортном протоколе. При клиентском рендеринге ипользуется AJAX через протокол HTTP, а при серверной отрисовке протокол HTTP исключается, как бы «дёргаются» роуты API напрямую, без curl.

Очень упрощённо так:

1. Определить роут для выдачи через AJAX
const app = express();
app.get('/api/get/some/data', (req, res) => {
const returnValue = db.get_some_data(); // упрощённо - можно извлечь данные из БД
res.send(returnValue);
});


2. При отрисовке на сервере вызвать его без AJAX, а в браузере — через AJAX
let someData;
if (isServer)
someData = await some_kind_of_http.get('/api/get/some/data');
else
someData = await true_http.get('/api/get/some/data');


Таким образом если HTML как то зависит от AJAX данных (список какой-нибудь или страница товара), то данные будут извлечены из db.
Тем более webpack-dev-server не запускается. Это что за такая штука, которую невозможности просто взять, установить и запустить! Просто элементарно взять, установить и запустить! Это вообще нормально? Разработка react-redux — это полная лажа!
Конечно, когда просто читаешь статью, всё здорово и замечательно. Можно написать хорошие и хвалебные комментарии в адрес этого стека технологий.

Но когда лезешь разбираться и пытаешься сделать сам, то тут возникают кучу проблем. Везде бардак. Сплошной бардак. Каждый пишет, как хочет. Нет единых стандартов. Какие-то библиотека работают так, какие-то эдак. Кучу настроек, в которых очень легко запутаться. И в каждую настройку надо вникать, чтобы понимать, что на самом деле происходит. А их туча! Могут работать по-разному. Нет элегантности, простоты и красоты — что всегда должно быть превыше всего в программировании.
в следующий раз сбор стека займет едва ли более 5 минут.

Каждый раз это занимает 2-3 дня, потому что с момента прошлой сборки стартера прошло не менее 6 месяцев, а то и год и ВСЁ УЖЕ УСТАРЕЛО!
Только полноправные пользователи могут оставлять комментарии. Войдите, пожалуйста.