Это вторая статья о Server Side Rendering и изоморфных/универсальных приложениях на React. Первая под названием "Упрощаем универсальное/изоморфное приложение на React + Router + Redux + Express" была больше про кастомное решение, эта же статья нацелена больше на тех, кому не хочется заморачиваться, а хочется готовое решение, с коммьюнити, и вообще поменьше головной боли с настройкой, отладкой, подбором библиотек и т.д.  +  В данной статье будем рассматривать Next.JS, который обладает преимуществами в виде отсутствия конфигурации, серверного рендеринга и готовой экосистемы. Из коробки Next.JS не умеет работать с Redux, поэтому в процессе написания пробного проекта я выделил получившийся общий код в отдельный репозиторий next-redux-wrapper, с помощью которого в этой статье мы и соберем приложение-пример на Next.JS + Redux. Для новопришедших напоминаю, что суть серверного рендеринга довольно проста: на сервере нам нужно определить на основе правил роутера, какой компонент будет показан на странице, выяснить, какие данные ему нужны для работы, запросить эти данные, отрендерить HTML, и выслать этот HTML вместе с данными на клиент. Сейчас стремительно набирают популярность решения типа Create React App, которые постулируют подход "ноль конфигурации", все ставится одной командой, работает прямо из коробки, но содержит кучу ограничений, к тому же серверным рендерингом не обладает совсем. Можете ознакомиться с моей статьей на эту тему: Что взять за основу React приложения. Для этого есть альтернативы типа Next.JS и Electrode. Почему бы не взять их и забыть о мучениях? Или не забыть? Может все только хуже станет. Но на самом деле все зависит от задачи, и чтобы быстро сляпать приложение обычно гибкости хватает, но вот некоторые ограничения, о которых стоит помнить, начиная работу с Next.JS: В процессе рендеринга страниц мини-роутер берет соответствующий файл из директории ./pages, берет из него default export и использует статичный метод getInitialProps из экспортированного компонента для того, чтоб забросить в него эти самые props. Метод может быть асинхронным, т.е. возвращать Promise. Этот метод вызывается как на сервере, так и на клиенте, разница только в количестве получаемых аргументов, например, на сервере есть req (который является NodeJS Request). И клиент и сервер получают нормализованные pathname и query. Примерно так выглядит код для страницы: Или в функциональном стиле: Статичный метод getInitialProps идеально подходит в качестве места, где мы можем отдиспатчить некоторые экшны, чтобы привести Redux Store в нужное состояние, чтоб затем во время рендеринга страница могла считать оттуда все нужные данные. Но для того, чтобы это заработало Store нужно где-то создать, причем и на сервере и на клиенте, причем на клиенте Store как правило всегда один, а на сервере он должен быть создан для каждого запроса отдельно. Помимо аргумента getInitialProps этот же Store должен быть предоставлен Redux Provider'у, чтобы все вложенные в него компоненты могли получить доступ к Store. Для этой цели лучше всего подходит концепция Higher Order Components. В двух слова это функция которая принимает компонент в качестве аргумента и возвращает его обернутую версию, которая тоже является компонентом, например React Router withRouter(Cmp). Иногда функция может принимать аргументы, и возвращать другую функцию, которая уже примет компонент, это называется каррирование, например React Redux connect(mapStateToProps, mapDispathToProps)(Cmp). Обертка, которую мы собираемся использовать, должна быть применена ко всем страницам, чтобы быть уверенным, что все начальные условия всегда одинаковы. Для начала, поставим все пакеты: Создадим вещи, необходимые для Redux: Теперь обернем страницу в next-redux-wrapper: Диспатчить можно и Promise, в этом случае нужно будет дождаться его завершения и уже тогда вернуть initial props. Вот, собственно, и все. Вся магия происходит внутри обертки, а снаружи мы видим чистую и красивую имплементацию. Полный пример можно посмотреть в репозитории Next.js: https://github.com/zeit/next.js/blob/master/examples/with-redux/README.md или посмотреть на пример в репозитории обертки: https://github.com/kirill-konshin/next-redux-wrapper/blob/master/pages/index.js. Для запуска достаточно просто написать в консоли: Я обнаружил один неприятный момент, если вы хотите использовать pages/_document.js (который позволяет собрать шаблон всей страницы) и диспатчить экшны из его getInitialProps, а также с конечной страницы, то может возникнуть race condition. Порядок вызова этих функций не особо контролируется и может так получиться, что они будут работать параллельно, поэтому в моменты когда рендерится шаблон страницы и сама страница состояние может быть разным. Сама обертка этот сценарий поддерживает, потому что она сохраняет Store в самом запросе, а также гарантирует, что на клиенте Store всегда один. Однако авторы Next.js говорят, что это плохая практика иметь диспатчи в _document.js. Они предлагают вместо этого написать еше один HOC, уже кастомный, и делать все там, но это остается за рамками статьи. Principal Software Developer