Николай Ланец
8 авг. 2017 г., 0:54

React-js. Урок №1. Быстрый старт.

В продолжение недавнего анонса про сайт на react-js, начинаю сегодня обещанный цикл обучающих статей по этим технологиям. Да, тут недостаточно изучить только реакт, это целый комплекс связанных между собой технологий:
  • node-js - серверная реализация javascript, основа всего, без чего ничто не будет работать, включая react. Здесь стоит сразу оговорочку сделать, что react может и просто в браузере работать, как обычная JS-библиотека, но мы будем учиться разрабатывать именно изоморфные JS-приложения, которые работать умеют не только в браузере, но и на сервере. А здесь уже как раз нужны будут все заявленные технологии.
  • webpack - сборщик скриптов (из кучи отдельных модулей собирает конечный JS/CSS). В MODX наверняка многие сталкивались с MinifyX. Условно можно сказать, что это схожие по назначению вещи, хоть и сильно отличаются. Привожу это сравнение просто для большего понимания для чего вообще нужен webpack.
  • react - JS-библиотека от фейсбука, реализующая такую интересную штуку как Virtual DOM. Многие спрашивают в чем отличие реакта от тех же Angular, Backbone и т.п. Коротко: react - это просто библиотека, а ангулар и т.п. - это комплексные фреймворки с кучей всего готового. С реактом старт сложнее (так как все необходимые под проект модули надо ставить самому, и много писать приходится), но зато потом и контроль проекта лучше, и вообще со временем многое получается писать гораздо быстрее.
Вообще все это очень объемно и сложно, но я попытаюсь предоставить такой материал, который позволит достаточно быстро погрузиться в это и уже сегодня запустить свое первое приложение, пропустив такие сложные этапы как настройка сервера, конфигурирование вебпака и т.п.

Здесь сразу уточню, что разработка на javascript сама по себе более проблемная, чем на том же php, просто потому что слишком большое разнообразие платформ используется, программ и т.п. То есть если вы настроили свой сервер и запустили там сайт на php, то для всех пользователей это один и тот же сервер, одна и та же программная среда. С JS же все гораздо хуже - куча браузеров, версии движка и т.п. По этой причине я изначально оговорю какие программные средства мы будем с вами использовать, и настоятельно рекомендую придерживаться именно этого списка, чтобы у вас не возникали лишние ошибки. Когда вы будете чувствовать себя уверенно во всем этом, тогда уже сможете пробовать настроить свой сервер, адаптировать все под различные браузеры и т.п. Сейчас у нас эти задачи не стоят, мы просто будем учиться для себя.

Итак, в качестве хостинга советую https://modhost.pro/ (в случае, если вы работаете с MODX. В остальных случаях все на ваше усмотрение). На первых порах вам будет достаточно даже бесплатного тарифного плана, 250 метров места хоть и под завязку нам, но все же хватает. Напоминаю, что бесплатный сервер живет только сутки, поэтому кому не хочется переустанавливать все с нуля каждый раз (хоть это и занимает 10 минут), оплатите минимальный тарифный план и учитесь спокойно.

Отмечу на будущее, что по мере появления новых уроков заготовка будет увеличиваться в объемах, и 250 метров на хостинге может просто не хватить и команда npm i будет в итоге разваливаться, так как не сможет установить все нужные пакеты. Если у вас это происходит, значит берите минимальный ТП, там 500 метров, их хватит.

На вопрос “Почему modhost, заточенный под MODX, если мы будем изучать реакт?” отвечу: на modhost сразу есть все необходимое: ssh, node-js, npm, git. Это все нам понадобится. Можно, конечно, альтернативы посмотреть, но в нашем случае овчинка совсем не стоит выделки.

В качестве браузера советую Google Chrome (да-да, теперь я в нем работаю, а не в FireFox). И в него же обязательно установите дополнение react-dev-tools. После установки у вас в дев-панели появится еще одна вкладка - react

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

В качестве редактора я использую sublime с установленными пакетами Babel (для подсветки react-кода) и LESS, но вы можете использовать любой другой редактор, поддерживающий синтаксис JSX (как тот же PhpStorm), это уже не принципиально.

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

Создаем сайт на modhost.pro

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

После того, как сайт был создан (в нашем случае это правильней назвать сервер), заходим в информацию о нем и находим данные авторизации по ssh


Каждый сам себе выбирает посредством чего подключаться по ssh, но на windows почти стандарт - putty. Я на линуксе сижу, поэтому использую его родной терминал.

Скачиваем файлы проекта.

Я собрал уже готовую заготовку и выложил ее на гит.
Коннектимся на сервер по ssh и прям в домашнем каталоге выполняем команду


В результате у вас в текущем каталоге появится папка react-study


Переходим в эту папку и выполняем команду npm i

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


Собственно, на этом все. Проект установлен, осталось его просто запустить. Выполняем npm start

Если сервер запустился, вы увидите такое:


Обратите внимание на указанный в сообщении порт. В данном случае 3000. Вот на этом порту у нас и будет крутиться приложение. Остается перейти в браузере на адрес вашего сайта (он указан в панели хостинга) и дописать ему в адресной строке порт. У меня все запустилось без проблем (не считая пары мелких варнингов)


На странице будет несколько кнопок, каждая из которых по клику открывает так называемый snackbar - это один из material-design элементов. Собственно, пример взят с сайта-документации библиотеки material-ui https://material-ui-1dab0.firebaseapp.com/component-demos/snackbars Советую внимательно изучить этот сайт, многие элементы мы будем брать именно оттуда. Вообще react+material-ui - это сила! Интерфейсы на этом писать - только в путь!

Структура и редактирование приложения.

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

Условный корень приложения - /app/modules/ReactStudy/components/App/index.js
а компонент с кнопками - /app/modules/ReactStudy/components/lessons/lesson1/index.js


Давайте разберем их подробней.

Вот код MainApp:
import './styles/styles.less'; import React, {Component} from "react"; import PropTypes from 'prop-types'; import Lesson1 from '../lessons/lesson1/'; export default class MainApp extends Component { constructor(props){ super(props); let state = { }; this.state = state; } render() { let { ...other } = this.props; return <div> <Lesson1 /> </div> } } MainApp.propTypes = { } MainApp.defaultProps = { }
Основные моменты:

1. Класс MainApp расширяет базовый класс Component из пакета react. Этот класс импортирован выше в строке import React, {Component} from "react";
Эту строку, условно, следует понимать следующим образом: “Импортировать переменные React и Component из стороннего модуля react”.
Основные методы класса React.Component описаны здесь: https://facebook.github.io/react/docs/react-component.html

2. Метод render(){} обязательный и должен возвращать или null (в случае, если мы ничего не хотим рендерить на странице), или node/react-элемент. В нашем случае мы возвращаем div с отрисованным в него компонентом Lesson1
return <div> <Lesson1 /> </div>
Компонент Lesson1 мы импортируем выше.
import Lesson1 from '../lessons/lesson1/';

Ну а теперь самое главное - это изучим компонент Lesson1, так как по сути именно его мы видим в окне браузера (точнее результат его работы).

Вот код этого компонента:
import React, { Component } from 'react'; import Button from 'material-ui/Button'; import Snackbar from 'material-ui/Snackbar'; class PositionedSnackbar extends Component { static state = { open: false, vertical: null, horizontal: null, }; handleClick = state => () => { this.setState({ open: true, ...state }); }; handleRequestClose = () => { this.setState({ open: false }); }; render() { const { vertical, horizontal, open } = this.state; return ( <div> <Button onClick={this.handleClick({ vertical: 'top', horizontal: 'center' })}> Top-Center </Button> <Button onClick={this.handleClick({ vertical: 'top', horizontal: 'right' })}> Top-Right </Button> <Button onClick={this.handleClick({ vertical: 'bottom', horizontal: 'right' })}> Bottom-Right </Button> <Button onClick={this.handleClick({ vertical: 'bottom', horizontal: 'center' })}> Bottom-Center </Button> <Button onClick={this.handleClick({ vertical: 'bottom', horizontal: 'left' })}> Bottom-Left </Button> <Button onClick={this.handleClick({ vertical: 'top', horizontal: 'left' })}> Top-Left </Button> <Snackbar anchorOrigin={{ vertical, horizontal }} open={open} onRequestClose={this.handleRequestClose} SnackbarContentProps={{ 'aria-describedby': 'message-id', }} message={<span id="message-id">I love snacks</span>} /> </div> ); } } export default PositionedSnackbar;
Здесь примерно все то же самое, что и в предыдущем листинге, только содержимое метода render() более объемное и появляется еще два метода handleClick() и handleRequestClose().

Отмечу, что метод render(), как и говорилось выше, - это “родной” метод класса React.Component, а вот два других, это уже наши произвольные методы, которые могут называться иначе, и методов этих может быть сколько угодно.

Итак, здесь не сложно выделить несколько обособленных элементов <Button />, которые рендерятся в кнопки как есть, и есть еще один элемент - <Snackbar />. Это отличный пример для демонстрации реакта и формирования понимания парадигмы его работы. Вот у нас на странице выводится 6 кнопок, по клику на каждую у нас в том или ином месте появляется элемент Snackbar с надписью I love snacks. Магия заключается в том, что хотя у нас 6 кнопок, в единицу времени у нас выводится только одна сущность снакбара, просто в разных местах ввиду того, что этому элементу передаются разные свойства, которые и определяют в каком месте этот снакбар будет выведен.

Здесь последует довольно объемное и запутанное объяснение всего этого с кучей повторений, но очень важно во все это вникнуть и понять, так как все это на 95% соответствует всему, что работает на реакте.

Свойства и состояния.

Это основа жизни реакт-компонентов. Хотя не все реакт-компоненты имеют свойства (props) и состояния (state), именно эти вещи изначально закладывались в саму основу реакт-парадигмы. То, что вы имеете в итоге на странице документа - это отражение текущего состояния элемента (state). Меняя его состояния из любого положения, это скорее всего повлияет на отображение его в документе. Давайте попробуем освоить это на практике. Откроем хром-консоль (F12), перейдем во вкладку react и посмотрим структуру react-элементов текущего документа. В нашем случае она вот такая: http://joxi.ru/Y2LjLVES9DL5Kr
То есть мы видим внутри других компонентов наши 6 кнопок и конечный снакбар. Так же мы видим, что у снакбара в Props свойство open является true. Попробуйте снять галочку с этого свойства, и снакбар скороется. И наоборот, выставив его в true, снакбар появится. Попробуйте вручную отредактировать свойства anchorOrigin, изменив, к примеру, left на right, и снакбар изменит свое положение.



Это и есть основа разработки на реакт - не поведение элемента определяет состояние, а наоборот, состояние определяет поведение элемента. Таким образом вы можете использовать одно хранилище состояний для управления сразу несколькими элементами (это мы рассмотрим позже, но сейчас об этом следует упомянуть, чтобы понимать, к чему стремиться).

А откуда берутся эти свойства?

Свойства могут быть прописаны по умолчанию, а могут быть переданы непосредственно в элемент. Давайте еще раз внимательно посмотрим на наш код компонента PositionedSnackbar.
В начале класса у нас прописаны исходные свойства состояния объекта.
state = { open: false, vertical: null, horizontal: null, };
Далее в методе render() мы “выдергиваем” из состояния этого объекта нужные нам переменные (получаем три отдельные переменные).
const { vertical, horizontal, open } = this.state;
И потом этим переменные передаем непосредственно в снакбар как свойства.
<Snackbar anchorOrigin={{ vertical, horizontal }} open={open} onRequestClose={this.handleRequestClose} SnackbarContentProps={{ 'aria-describedby': 'message-id', }} message={<span id="message-id">I love snacks</span>} />
То есть переменные vertical и horizontal определяют положение снакбара, а свойство open определяет открыт ли снакбар или закрыт. Соответственно, если свойство open == true, то снакбар появится на странице.

И вот здесь еще один очень-очень важный момент. Многие в начале освоения реакта воспринимают код метода render() неправильно, как декларативный, то есть прописали компонент со статическими переменными и все, он один раз инициировался и далее живет своей жизнью. Это неправильно. Магия заключается в том, что метод render() вызывается за время жизни конечного объекта чаще всего далеко не один раз. При любом изменении входящих свойств элемента (props) или его состояния (state) метод render() вызывается по новой. И если какое-то свойство изменилось и влияет на поведение дочерних объектов или текущего, то документ будет перерисован, то есть если поменялось, к примеру, свойство open, то снакбар будет открыт или скрыт, то есть он переинициализируется по новой.

И тут как раз появляется Virtual DOM. Если бы каждое изменение свойств объектов с последующим вызовом метода render() приводило к тому, что DOM документа перерисовывался бы по новой, это бы повлекло бы за собой существенное снижение производительности такого веб-приложения. Все же работает гораздо хитрее - реакт сначала выполняет в своей виртуальной машине все вычисления и смотрит, есть ли какие-то изменения в конечном DOM элемента, и если есть, тогда меняет и DOM документа. А если нет, то нет. Получается и гибкость высокая, и производительность норм.

Итак, давайте еще раз посмотрим в react-dev-tools, только на этот раз не на конечный элемент Snackbar, а на главный компонент PositionedSnackbar, на его свойства.


Сейчас мы видим, что state.open == false. Кстати, если в дев-панели нажать Esc, откроется допвкладка Console, в которой можно выполнять JS-команды, и в ней выбранный реакт-элемент будет виден как переменная $r, с которой можно работать напрямую.


Выполнив команду, мы видим уже свойства элемента как самостоятельные JS-сущности


И если там же выполнить команды $r.state.open = true (то есть установить новое значение состояния) и потом выполнить $r.forceUpdate() (то есть вызывать принудительный ререндеринг компонента), то мы увидим и изменения на странице.


В данном случае мы выполняли два отдельных метода (сначала изменили состояние объекта, а потом вызвали его ререндеринг). Но это только потому что мы изменили свойство состояния напрямую. Но правильней такие вещи делать через метод setState(newState). В нашем случае это $r.setState({open: true});


Такой метод не только поменяет состояние, но и вызовет последовательную цепочку событий жизненного цикла реакт-компонента. Это мы разберем позже, но коротко: будет вызван метод shouldComponentUpdate(nextProps, nextState), на уровне которого вы можете регулировать стоит ли обновить состояние компонента или нет (в нашем случае не уровне разницы текущего состояния this.state и предстоящего состояния nextState), то есть вы можете как позволить компоненту измениться, так и препятствовать этому. Затем вызывается componentDidUpdate(prevProps, prevState), то есть когда компонент был обновлен, и опять-таки что-то выполнить по результату, имея разницу предыдущего состояния и текущего. Затем метод componentWillReceiveProps(newProps) у всех дочерних элементов, которые вы вызываете в своем компоненте и которым передаете различные свойства (и уже на уровне тех компонентов все те же перечисленные методы). То есть это целый конвейер. На первый взгляд все это очень сложно и запутанно, но когда разберетесь, почувствуете насколько управляемыми и предсказуемыми станут ваши программные продукты.

Так вот, мы изменили программно состояние нашего компонента PositionedSnackbar, и его эти свойства состояния в виде передаваемых параметров (props) ушли далее в компонент Snackbar.



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

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

Забегая вперед, что скоро появится готовая сборка MODX+node.js+react, на базе чего будет строить многое, так что этот цикл статей не просто так появляется, а закладывается в основу многих, в том числе и наших, технологий на ближайшие пару лет. Так что как минимум поверхностно изучить не будет лишним.


Супер, трендовая тема!
Вопрос: Насколько хорошая поддержка браузерами react без node.js ?


MODX+node.js+react сомневаюсь что в таком стэке получится организовать корректный serverside rendering + code splitting, разве что modx будет в роли REST сервиса, в который будет стучаться node.js, но это оверхед...не говоря уже о том, что разрабатывая приложения на реакте прикручивать к нему тяжелую "админку" modx для управления ресурсами - крайне гиблое тяжело поддерживаемое дело...хотя я всей душой люблю реакт и modx
> Вопрос: Насколько хорошая поддержка браузерами react без node.js ?
Это не связанные друг с другом вещи. Реакт работает сам по себе. Нода нужна только для сборки конечного кода, если мы модульно все собираем. А так браузер вообще не знает есть там нода или нет.
Но предполагаю, что ваш вопрос про то, есть ли разница когда конечный HTML отрисован на стороне сервера, а когда нет. Тут браузеру опять-таки фиолетово. Даже если код отрисован, все равно после загрузки должен реакт сработать, и если что-то не так, страница будет статической, реакт работать не будет.
> MODX+node.js+react сомневаюсь что в таком стэке получится организовать корректный serverside rendering + code splitting, разве что modx будет в роли REST сервиса, в который будет стучаться node.js, но это оверхед
Посмотрите на сайт Клуба. Все работает замечательно.
> Посмотрите на сайт Клуба. Все работает замечательно.
не вижу никакого code splitting на сайте клуба и async components, у вас кстати на продакшен бандле висит logger и в консоль кидает экшены пользователям...роутинг у вас на клиенте и сообщает ноде о смене VIEW, вы стучитесь в тяжеленный modx, который стучится в одну таблицу для получения документа, подгрузки всех связанных и необходимых TV полей за один запрос...все хорошо работает пока у вас в сторе несколько сущностей, а если их будет сотня...а что с подходом в целом? у вас доп запросы все идут к одному коннектору, что противоречит в принципе архитектуре REST с его endpoint'ами...зачем использовать то что принадлежит веб-приложениям, если не соблюдать базовые принципы построения веб-приложений...в целом конечно труд достойный, но все же сизифов труд.
Николай, здравствуйте. Обязательно буду следить за вашим курсом и по возможности повторять описанное. Меня тоже заинтересовал момент про modx+node.js+react. А какая здесь роль отведена модексу? На сколько мне известно node.js вполне покрывает потребности бэкенда. Модекс только ради админки, чтобы свою не пилить или на него еще какие-то функции возлагаются?
Нахожу этот курс очень полезным и интересным, Николай. Буду ждать следующих уроков и проходить этот.
> сомневаюсь что в таком стэке получится организовать корректный serverside rendering + code splitting
> не вижу никакого code splitting на сайте клуба и async components
code splitting я не докручивал, необходимости не было у меня. А вот serverside rendering работает.

> у вас кстати на продакшен бандле висит logger и в консоль кидает экшены пользователям...
И пусть кидает. Он мне здесь для отладки, я же многое здесь в первую очередь экспериментирую.

> роутинг у вас на клиенте
Не правда. На сайте Клуба роутинг на стороне сервера. С компонентами некоторая заморочка имеется, но роутинг MODX обрабатывает.

Про REST и т.п. - мне в данном случае удобней было именно так делать.

В целом, если вы знаете как все это делать лучше: велкам, можете так же написать свои статьи. Иначе, без подробного туториала, вряд ли получится понять что именно вы имеете ввиду и в чем разница. Принцип: возражаешь - предлагай.
> А какая здесь роль отведена модексу? .... Модекс только ради админки, чтобы свою не пилить или на него еще какие-то функции возлагаются?

Да, типа того. Ведь MODX это и политики безопасности, и плагины всякие, и модель базы данных и т.д. и т.п. Вот представь, у тебя задача посадить имеющийся MODX-сайт на эти технологии. Что, переписывать весь сайт с нуля на JS? А пользователи? А заказы? А плагины и т.п.? Нет, очень сомнительная затея. Как мне видится, здесь реакт и т.п. именно идет как фронт для MODX-а, а не полная альтернатива ему.
> Да, типа того. Ведь MODX это и политики безопасности, и плагины всякие, и модель базы данных и т.д. и т.п. Вот представь, у тебя задача посадить имеющийся MODX-сайт на эти технологии. Что, переписывать весь сайт с нуля на JS? А пользователи? А заказы? А плагины и т.п.? Нет, очень сомнительная затея. Как мне видится, здесь реакт и т.п. именно идет как фронт для MODX-а, а не полная альтернатива ему.

Вот именно по этой причине это и есть достойный труд! Подружить бы идеологию modx с реактом с учетом всех архитектурных стандартов - это будет мощнейший комбайн на длительный срок, бомба!!! еще бы админка modx на реакте была!)))
> Да, типа того. Ведь MODX это и политики безопасности, и плагины всякие, и модель базы данных и т.д. и т.п.
Ну я так и подумал сразу такой себе "хак" для того, чтобы с нуля меньше писать, а тут все-таки "технология отработана", но и предположил, что возможно еще есть какие-то причины. Просто тут некий диссонанс возникает — фронт летает как ракета и "тяжеленная" админка модекса, но это уже мои личные заморочки.
Еще было бы классно, если бы вы описали такие моменты в этой связке:
1. MODX (или php) какую роль в приложении выполняет часть написанная на php
2. Какую роль выполняет часть написанная на Node.js
3. С react'ом более менее ясно, но не очень понятно как он взаимодействует с MODX или php и Node.js
4. Как все это дело взаимодействует в куче и почему именно эти технологии.
> это будет мощнейший комбайн на длительный срок, бомба!!!
Именно так. Я считаю, что даже если сейчас MODX в развитии сам по себе встанет колом, за счет примеси этих современных технологий можно легко рассчитывать еще минимум на 2-3 года актуальности.
> еще бы админка modx на реакте была!)))
Я думаю над этим, и некоторые наработки у меня уже есть.
> Просто тут некий диссонанс возникает — фронт летает как ракета и "тяжеленная" админка модекса, но это уже мои личные заморочки.
На скорость смотреть не совсем правильно. Лично для меня реакт - это в первую очередь более удобная среда разработки. MODX был и есть без как такового фронта, каждый крутит свое на что горазд (кто чистый HTML+CSS фигачит, кто LESS-SASS примешивает и собирает by MinifyX), кто готовые темы как может прикручивает. Так что проблема фронта для MODX всегда актуальна была. Я не решаю эту проблему, но мне с этим работать удобней и перспективней. Ведь на выходе получаются веб-приложения, которые могут работать не только с MODX.

Все остальное будет описано позже. Вот это вводная статья, самый минимум. А какой объем получился? Чтобы описать даже начальный уровень, придется написать еще минимум десяток таких статей, а то и не один. В дальнейшем последовательно мы все вопросы эти разберем.
> фронт летает как ракета и "тяжеленная" админка модекса, но это уже мои личные заморочки.
это не ваши личные заморочки - это горькая правда...админка modx нуждается в в полной переработке с учетом современных стандартов. там еще и старая версия extjs все усугубляет. PS в комментариях выше описана техника подхода к реализации такого гибридного приложения

> Именно так. Я считаю, что даже если сейчас MODX в развитии сам по себе встанет колом, за счет примеси этих современных технологий можно легко рассчитывать еще минимум на 2-3 года актуальности.
Если мыслить еще глубже и научить modx работать с компонентами, а точнее научить компоненты modx работать c компонентами react - вот это сильно! устанавливаешь один компонент, он вживляется и в modx, имеет асинхронный код компонента для клиента и появившись на странице, динамично загружается - и так со всеми компонентами)) фантазия разыгралась))) я с вами!)

> Если мыслить еще глубже и научить modx работать с компонентами, а точнее научить компоненты modx работать c компонентами react - вот это сильно!
Вот это уже точно лишнее будет :)
Собственно видим как на наших глазах рождаются инновации в деле сайтостроительства с помощью в т.ч. MODX. Хорошие комментарии от всех и отличный урок получился! Ждем продолжения и углубления в тему.

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

Уверен на 100% - это начало большого и интересного пути:
> Я не решаю эту проблему, но мне с этим работать удобней и перспективней. Ведь на выходе получаются веб-приложения, которые могут работать не только с MODX.


Ну, на сайте Клуба эти изменения заметны? :)
Здравствуйте. Все о теории да о теории, а я скажу, буквально сейчас соединил одно с другим. Могу сказать по опыту, все вполне "склеивается" если знать как :-). У меня получился следующий стек:
ReactJS + NodeJS + GraphQL + Modx. ServerSide Rendering на Node выполняет изоморфный бандл который выполняется после и на клиенте. Rest у Modx просто ужасный, да и вообще сам Rest уже отмирающий способ взаимодействия с сервером, я использую GraphQL для получения данных в клиентском приложении (ну и в SSR, один и тот же бандл ведь), Modx только как админка. Для корректного рендеринга компонентов используя MigX + Custom TV накидал сборку контентной части так же компонентами, в клиент уходит JSON со списком компонентов и данными к ним. В общем получилось вполне не плохо, использовал Modx так как много подходящих наработок было. ExtJS просто катастрофа, я бы его сжег, в остальном все вполне собралось и заработало :-).
И да, соглашусь с мнением выше, Modx к сожалению сильно устарел, несмотря на то, что работы над ним ведутся достаточно активно. Под капот заглядываешь и плакать хочется... Что касается менеджера, тоже соглашусь, ExtJS сам по себе весьма плох, старая версия все сильно усугубляет. Весь фреймворк в целом требует полной переработки на мой взгляд и с точки зрения архитектуры и с точки зрения реализации. Время идет, давно появились PSR стандарты, замечательная вещь, просто необходимая я считаю как для универсальности интерфейсов и разных механизмов так и для повышения культуры кода. Если кто не смотрит, загляните в расширения, кровь из глаз... Такое нельзя на свет выпускать :-).

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

В любом случае автору за статью спасибо. Хотел бы один момент отметить, вот так объявлять state неправильно:
class PositionedSnackbar extends Component { state = { open: false, vertical: null, horizontal: null, };
Правильно в конструкторе:
class PositionedSnackbar extends Component { constructor(props) { super(props); this.state = { open: false, vertical: null, horizontal: null, }; }
Вот пример в доке:
> Хотел бы один момент отметить, вот так объявлять state неправильно:

На самом деле это не мой код, а как я и писал выше, это официальный пример :) https://github.com/callemall/material-ui/blob/v1.0.0-beta.6/docs/src/pages/demos/snackbars/SimpleSnackbar.js#L19

Но вам плюс, что вы заметили. Хотя правильно не только так, как вы сказали. Вполне достаточно прописать static state. Я вам более скажу, можно вот так:
import lodash from 'lodash'; import DataView from '../'; let defaultProps = lodash.cloneDeep(DataView.defaultProps); defaultProps = Object.assign(defaultProps, { connector_path: 'deals', }); export default class DealsDataView extends DataView{ // ............... } DealsDataView.defaultProps = defaultProps;
Это я расширяю свойства по умолчанию, полученные от базового класса, можно так же и стейты расширять. Согласитесь, если это засунуть в constructor, не получится их так дернуть, пока он не инициализирован. Здесь просто надо изначально ориентироваться, какие свойства и состояния будут требовать инициализации объекта, а какие нет. А так это синтаксический сахар, выполнение одной и той же задачи с одним и тем же результатом, просто по-разному. А официальная документация реакта, как хорошо заметили на хабре - написана как поток сознания :) Там не все показано.





По поводу остального не буду спорить, у каждого свое видение. Скажу только от себя, почему я еще буду пока использовать MODX. GraphQL у меня пока еще только на очереди, надо будет выделить время на его освоение обязательно, но я не знаю что у него с политиками безопасности. Если бы вопрос просто стоял в формировании запросов, я вполне мог бы заюзать тот же knex и не париться. Но вопрос же еще в политиках безопасности. Далеко ходить не надо, здесь же есть закрытые блоги с различными уровнями доступа как минимум. То есть активно используется $modx->hasPermission(). Пока я не увидел для себя замену этому.
> Согласитесь, если это засунуть в constructor, не получится их так дернуть
Ну state это пропс объекта, но не пропс класса :-). Пропс класса конечно в конструкторе добавлять не нужно :-). Там тоже есть варианты в соответствии с тем как у Вас babel настроен :-). Вообще это же JavaScript можно еще много как писать, просто я указал на вариант ближе к спецификации ecma 2015 ( http://2ality.com/2015/02/es6-classes-final.html ). На мой взгляд, если мы все будем придерживаться правил, код станет более дружественным :-). Но я конечно же не против, просто свое мнение высказал :-). В общем-то тоже самое и я о Modx думаю, поэтому очень не нравится отсутствие каких либо движений со стороны сообщества в этом направлении ))).

> GraphQL у меня пока еще только на очереди
Можно разруливать по безопасности сделав взаимодействие с modx в resolve методах или middleware накрутить, можно еще вариантов напридумывать :-). В любом случае очень рекомендую, крайне удобный инструмент!!!

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

Я вижу Вы поправили и сделали state static свойством. Так не будет работать :-). Это именно свойство объекта, а static это свойство класса Вы объявили и внутри объекта оно будет доступно через this.constructor .

> И если там же выполнить команды $r.state.open = true (то есть установить новое значение состояния) и потом выполнить $r.forceUpdate()
Лучше просто $r.setState({ open: true }); оно обновит значение состояния и сразу приведет к автоматическому перерендеру, без форса ). Ребята из фейсбука не рекомендуют forceUpdate использовать :-)
>>> Я вижу Вы поправили и сделали state static свойством. Так не будет работать :-). Это именно свойство объекта, а static это свойство класса Вы объявили и внутри объекта оно будет доступно через this.constructor .

Ага, не будет так работать :) Короче спал мало, и запутался с этим пропсами, стейтами, контекстами и т.п. :) Короче, вот так работает:
export default class PartnersByCategoryBlock extends Component{ static propTypes = { }; static contextTypes = { request: PropTypes.func.isRequired, }; constructor(props){ super(props); this.state = { partners: [], partnersCategories: [], category: null, } } render(){ return <div>Some content</div> } }



Кстати, что думаете на счет этого эксперимента-костыля? https://modxclub.ru/topics/eksperiment.-korzina-dlya-minishop2-na-reakte-2689.html
а лучше вообще в конструкторе this.state = this.getInitialState() а getInitialState возвращает объект :) я почему-то приучился так делать)

PS
Артем Гончарук, интересно посмотреть на ваш вариант...именно так мне и думалось объединение реакта с modx
Почитал про GraphQL поподробней. Да, очень интересная штука. В самое ближайшее время поковыряю. Уже есть несколько идей интересных. Вообще, сначала я освоил react. Затем flux, и был очень рад! Локальных стейтов и пропсов было мало, и не хватало более функциональных хранилищ. На самом деле еще redux освоил, но остановился на флюксе по ряду причин. Но это все на клиенте (состояния и отображения). Не хватало еще API для запросов. И вот чувствую, что мое счастье близко :)
> а лучше вообще в конструкторе this.state = this.getInitialState() а getInitialState возвращает объект :) я почему-то приучился так делать)
getInitialState более не используется, это для ES5 варианта было на старых версиях react ). Достаточно просто присвоить объект к this.state как в предыдущем комментарии у Николая :-). В документации на фейсбук много примеров :-).

> Не хватало еще API для запросов.
Я использую redux и smart actions в которых делаю запросы в graphQL, результат новым экшном кладу в store. Вообще можно использовать redux saga, она использует генераторы по сути делает тоже самое, но с разницей, экшны у саги должны быть тупые :-). Мы с товарищем на одном проекте на react-native используем сагу, на том, что с модх соединяли без саги, есть просто экшны которые диспатчатся при входе на определенный локейшн, в них делается запрос и результат кладется в store, далее каскадный апдейт автоматом происходит. Грубо говоря у каждого могуля (страницы) есть свой экшн входа который активируется при монтировании модуля, активируется роутером, а GraphQL позволяет одним запросом получить все необходимые данные для всех компонентов сразу, ну и за счет асинхронности (nodejs имплементация) внутри самого GraphQL часть запросов может выполняться параллельно, через несколько коннекшнов к базе, что повышает производительность.

> Кстати, что думаете на счет этого эксперимента-костыля
Думаю, прекрасно, что сообщество в целом наконец стало интересоваться данной технологией и массово ее использовать :-). Я пока внимательно не знакомился с трудом, если желаете могу позже оставить свое мнение. Беглый взгляд говорит, что вот эта конструкция описанная в статье: window.FrontBasketTemplates = FrontBasketTemplates; вызовет падение ноды при серверном рендеринге, поскольку в глобал скойпе не будет window :-).


Еще хотел бы добавить свое мнение на тему обмена данными между компонентами. Если использовать единый стор, отпадает необходимость писать хоть какое-либо межкомпонентное взаимодействие, по сути его и не должно быть, это противоречит компонентному подходу, компонент не должен знать о том, что есть кто-то еще, он должен получать данные, отрисовываться и если нужно выполнять какую-то свою логику. В едином сторе лежат данные которые мы можем получать прикручивая компоненты к тем или иным узлам стора, тогда в большенстве случаев и потребность в жизненном цикле отпадает и компоненты становятся стрелочными, работают быстрее и проверяют сами нужно ли им отрисовываться по пропсам. Да кстати рекомендую использовать immutablejs и хранить данные в таком представлении, тогда проверка необходимости рендеринга работает лучше, соответственно все приложение работает быстрей.
>> Беглый взгляд говорит, что вот эта конструкция описанная в статье: window.FrontBasketTemplates = FrontBasketTemplates; вызовет падение ноды при серверном рендеринге, поскольку в глобал скойпе не будет window :-).
Да, так и есть, упадет :) К слову, совсем недавно воевал здесь же с редактором draft-js, мне на сервере надо было генерить стейт методом convertFromHTML, так вот ему нужен DOM:) Помог domjs, очень крутая штука :) Но если бы у меня был ssr, нафиг я бы вообще заморочился с жучкой?))) Это эксперимент был. Хотя думаю, что-то из этого все-таки выкатит, есть у меня пара идей.
>>> Еще хотел бы добавить свое мнение на тему обмена данными между компонентами.
Я позже напишу довольно комплексную статью что и как я использую. Но скорее всего это будет после еще нескольких статей про реакт, так как многие просто не моймут о чем речь. Но коротко расскажу что у меня и как.
Во-первых, как я и говорил, я не использую редукс. Пара основных причин:
1. Не копал очень глубоко, но мое предположение, что он использует что-то типа [а-ля lodash].cloneDeep, скорее всего для того, чтобы иметь разницу состояний, чтобы можно было потом эту разницу отловиться в методах типа componentWillReceiveProps. Из-за этого я наблюдал кучу тормозов в своих интерфейсах, для устранения которых приходилось исхищряться с методами типаshouldComponentUpdate.
2. Мне не нравится эта децинтрализованность в описании редюсеров и экценов.

В итоге я перешел на flux (который как раз и использует immutablejs). Но и его я чуть под себя модифицировал. Теперь у меня примерно такие конструкции используются:
store.getDispatcher().dispatch(store.actions['UPDATE'], item, newState);
Как видно, тот пляшешь от одного исходного объекта store. И это я еще оптимизирую, чтобы вовсе было store.reduce("UPDATE", item, data);

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

Есть несколько преимуществ в этом.

1. Самое главное: я в хранилищах могу хранить реальные действующие объекты и в дальнейшем обновлять их как напрямую (если очень хочется), так и находить их с помощью соответствия имеющегося объекта. Пример:
items.map(item => <Button onClick={event => {store.getState().find(n => n === item)}.do(...)} >{item.name} </Button>)
Мне не нужно в таком случае никаких идентификаторов. Именно это позволяет создавать сразу несколько временных объектов, даже пустых, а потом нелинейно их редактировать.

2. Производительность. Из-за отсутствия каких-либо операций копирования, даже множественные обходы не дают какой-то ощутимой нагрузки.

>> отпадает необходимость писать хоть какое-либо межкомпонентное взаимодействие
Забыл сказать. Многие базовые методы и свойства, которые нужны в распределенных компонентах, я вообще переношу в контексты. Вот уж точно сильная штука. Хотя фейсбук не рекомендует использовать контексты и грозятся, что в будущем они исчезнут, я сомневаюсь, что они просто так дадут какую-то замену этому. Слишком многое на этом работает.

getInitialState не для этого....зачастую требуется сбросить стэйт у компонентов, и вызов this.setState(this.getInitialState()) не мутирует текущий стэйт а заменит его полностью первоначальным
Николай, я лично не вижу ничего ужасного в использовании SCU(shouldComponentUpdate)....и даже больше, считаю что его необходимо использовать для предотвращения многочисленных рендеров и сравнения виртуалДома...
Использование стрелочных компонентов также в любом случае заставляет реакт сравнить новый дом с виртуальным...имхо любой компонент не требуется рендерить тогда, когда это не надо для этого компонента.
Романом, во всем с Вами согласен )). Да вариант хороший со сбросом стейта, я так никогда не делал, я просто стейтлес компоненты делаю, соответственно мне не приходится сбрасывать стейты, так как стейтов вообще нет :-D.

Николай, думаю если Вы углубитесь, возможно измените свое мнение в отношении Редакса. Все, что Вы привели можно делать и с редаксом и компоненты можно монтировать к узлам стора точечьно и имутабл объекты хранить и все что угодно :-). Если стор сделать имутабельным проблем с производительностью не будет, ну и конечно же в компонентах использующих жизненный цикл использовать SCU не просто не страшно, а очень даже нужно, если у Вас компонент содержит внутренние стейты :-).

Николай, посмотрите про машину времени ))). Мотаем туда сюда, мутируем стор в ретроспективу, сохраняем состояние стора, импонт / экспорт, фиксация стора. Redux Dev Tools, посмотрите, шикарная штука, особенно когда дебаг нужен. Зафиксировал стор, обновляешь страницу, а все в нужном состоянии )).
я вот хоть и реализовал несколько проектов на реакте все равно не могу пока понять одного...а зачем иммутабельный стор? в моем случае после того как диспатчится экшен, его ловит редьюсер, а редьюсер по сути своей не меняет старый стор...а создает новый объект и копирует в него информацию из старого...получается мы заменяем кусок стора полностью...

Выглядит это так

case CREATE_CATEGORY_SUCCESS: let newList = state.list; newList.unshift(action.data); return { ...state, list: newList, lastUpdated: + new Date()};
>> Николай, я лично не вижу ничего ужасного в использовании SCU(shouldComponentUpdate).
Так я тоже не имею ничего против. Но я сторонник того, чтобы средства оптимизации оставлять на потом, когда реально возникнет необходимость. Если уже сейчас вы используете все средства оптимизации, то что вы сделаете тогда, когда производительность будет низкая, а все средства уже использованы?
Второй момент: разрастается код и усложняется процесс. Этот механизм - очень тонкий, и приходится много условий прописывать. Если еще сложные связи, то вообще. Я вот сторонник того, чтобы кода было как можно меньше.
>> а редьюсер по сути своей не меняет старый стор...а создает новый объект и копирует в него информацию из старого...получается мы заменяем кусок стора полностью...
Вот как раз это копирование мне очень сильно не нравится по двум описанным выше причинам.
И все-таки, вот так работает :)
export default class Lesson1Page extends Page { state = { open: false, vertical: null, horizontal: null, };

> И все-таки, вот так работает :)
Работает конечно, я говорил, что спецификация рекомендует пропсы в конструкторе объявлять ).

Добавить комментарий