Как стать автором
Обновить

Комментарии 44

Ну зачем же представлять Господа, как злого и мстительного тимлида, навязывающего всем свое мнение?)))
Ну или даже вот такой вопрос — зачем писать используя Flux и называть это MVC? По сути у вас все view обращаются к одной модели через контроллеры этих вьюх посредством кидания экшнов, а модель автоматически обновляет вьюхи… эм, ну то есть у вас однонаправленное движение данных, как и нужно в Flux.
А кто сказал, что однонаправленное движение противоречит MVC? Такое придумано далеко не во Flux и применяется давным давно.
простите, не туда откомментировал :)
А зачем строить MVC на редуксе с реактом когда их связка создана ради Flux-архитектуры? А если нет возможности использовать Flux то зачем использовать редукс когда можно использовать что-либо более MVC с реактом?

Мне кажется, что я использую связку React+Redux вполне стандартным образом. Просто я ввел некоторые ограничения, которые гарантируют, что все плюсы, которые дает нам Redux не будут сведены на нет. Про возможное убийство плюсов я писал.
Насчет "если нет возможности использовать Flux то зачем использовать редукс" не могу согласиться. Redux — всё-таки это не Flux (про отличия от предшественников можно почитать тут https://github.com/reactjs/redux/blob/master/docs/introduction/PriorArt.md )
Зачем строить MVC? Потому что плюсов масса, и потому что это легко сделать — посмотрите код всего приложения, там никаких кастомных решений нет — всё беру из коробки.

Ну вы и не строите здесь MVC, а просто используете терминологию. Причем тут MVC вообще? Тут он никак не подходит. Если проводить аналогию, React-компоненты — это View+Controller (с точки зрения UI), а с точки зрения Redux-приложения — уже редьюсеры/mapPropsToState — это «контроллер» (но даже язык не поворачивается это так называть). Модель — ее тут вообще нет. Есть состояние, и есть контейнер состояния (Redux), реализующий Flux-паттерн. И зачем пытаться подогнать это под совершенно иной и не имеющий отношение к делу паттерн? Какие плюсы в назывании всего этого MVC?

Вы говорите, что модели здесь вообще нет.


Описание модели из русской википедии:


Модель (англ. Model):

Предоставляет знания: данные и методы работы с этими данными;
Реагирует на запросы, изменяя своё состояние;
Не содержит информации, как эти знания можно визуализировать;

Экземпляр Store из Redux:
Предоставляет знания: данные и методы работы с этими данными — YES
Реагирует на запросы, изменяя своё состояние — YES
Не содержит информации, как эти знания можно визуализировать — YES


Не вижу противоречий.


Controller связывавает View и Model. Здесь всё стандартно


Дело в том, что React-компонент может выполнять все роли из MVC — хранить данные, обрабытывать их и рулить другими компонентами, может даже дергать функции дочерних компонентов через Refs. Из-за таких широких возможностей многие лепят архитектуру как попало. Я же предлагаю вариант наведения в коде порядка, спроецировав старые добрые принципы в новую реальность

Дело в том, что React-компонент может выполнять все роли из MVC
Вот поэтому тут не стоит даже упоминать MVC, чтобы избежать любой путаницы.

Если лезть в детали — Store не меняет свое состояние сам, и не содержит сам по себе никаких методов работы с этими данными. Более того, там могут храниться как данные приложения (domain data), так и различные аспекты состояния UI. Redux — это «state container», а не модель. И не пытается быть последней. Редьюсеры меняют любое состояние, и отнюдь не обязательно связанное с бизнес-логикой приложения. С очень большой натяжкой можно назвать всю эту часть «моделью», и то только для тех, кто вот без MVC вообще не может понять что за что отвечает. Но, как по мне, это изначально неверный способ думать об архитектуре Redux-приложений.
Если лезть в детали — Store не меняет свое состояние сам, и не содержит сам по себе никаких методов работы с этими данными

Если уж совсем грубо, то это ужасная «тонкая модель» из мира MVC (не академического, а того, который часто реализуют на практике). А компоненты на реакте — вьюшка и толстый контроллер.
Я понимаю, что хочется казаться модным и утверждать, что здесь нет ничего из поганого мира ооп, но то, что вы старательно даете старым вещам новые имена не делает эти вещи новыми.
Да Redux — это вообще не модель, ни тонкая, ни какая-то еще (в терминах привычных реализаций MVC).

Да не, модным никто и не пытается казаться, и кто-то говорил что-то про «поганый мир ООП» (я так не считаю)? Я только о том, что не нужно все под одну гребенку MVC, потому что так вроде бы проще. Элементов больше, стыки между M, V и C без натяжки не сделать, да и незачем.
Я еще раз внимательно перечитал ваш пост — да, вы на самом деле используете редукс так же как и предлагает документация, только называете все терминами MVC. Не могли бы вы привести пример использования middlewares в том же ключе MVC? Просто я в вашем коде вижу чистейший флюкс (ну хорошо, да, не флюкс а редукс, но все же) просто с подогнанными названиями из MVC которые очень хорошо легли на пример из вашей статьи. И в связи с этим ощущаю некий диссонанс :)

Насчет middlewares: я использую redux-thunk. Без проблем прикрутится redux-devtools, какой-нибудь логгер и т.д… По-идее middlewares не должны мешать


Пример использования redux-thunk:


function submit() {
   return function(dispatch, getState) {
      const state = getState();
      dispatch(reset());  
      request('/auth/', {send: {
          login : state.login,
          password : state.password
      }}).then(function() {
          router.push('/');
      }).catch(function() {
          window.alert("Auth failed")
      });
   }
}
При том, что статья полезная и шикарная, не могу согласиться, что называть это MVC — корректно. Да, провести аналогии можно, но не более. MVC — это подход, который подразумевает множество потоков данных между множеством моделей и отображений через множество контроллеров. Flux же — это идея о «едином источнике правды» и как следствие об однонаправленном потоке данных. Хоть Вы выше и говорите, что Redux не Flux, но это правда лишь от части. Redux реализует Flux (как идею), но не реализует её так же как эту идею реализует библиотека Flux. В общем и целом, я о том что нужно называть вещи своими именами.
MVC — это разделение на M, V и С. А если коротко, то — разделение данных и представления. Ничего больше в этом подходе не подразумевается.

То что подход многими реализуется через одно место, вовсе не означает, что это подразумевается подходом.
Не согласен с утверждением. MVC все же определяет конкретные блоки. Кроме того, в статье «лейблы» M, V и C вешаются на вполне конкретные вещи, так что стоит рассматривать паттерн именно в контексте конкретной реализации (а их у MVC может быть полно).
разделение данных и представления

Очень грубое упрощение. MVC про разделение бизнес-логики (с данными) и логики представления (может быть тоже со своими данными).

Тогда MVVM — тоже MVC? Document-Model — тоже MVC?
Redux — это не совсем MVC, но выполняет ту же функцию: разделяет данные, представления и изменения данных.

Не стоит делать MVC потому что это легко, лучше, наоборот, MVC — чтобы хоть как то организовать архитектуру и чтобы далее было легче.
Так «архитектуру организовывают» компонентный подход, Flux-паттерн (односторонний data-flow, предсказуемое управление состоянием), причем тут MVC-то?

Про "легко" и "MVC" я написал, потому что для реализации его принципов в связке React и Redux не нужно никаких надстроек, не нужно своего кастомного кода — нужно лишь придерживаться пары простых принципов, как раз для хорошей организации кода

НЛО прилетело и опубликовало эту надпись здесь
Если уж приложение растет, никто не запрещает сделать 20 редьюсеров. А так же, использование библиотек вроде normalizr и reselect подразумевается априори.
НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

НЛО прилетело и опубликовало эту надпись здесь

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

При использовании Stateful-компонентов, чтобы достать их State, придется использовать Refs.

вы хотите зачем-то знать state дочерних компонентов? зачем использовать refs для того, чтобы читать this.state?

Я этого не хочу, но я видел, что так делают, а React это позволяет. Это одна из причин, которая заставила задуматься о том, как пресечь это на корню

Хранение каких-то данных в представлении или контроллере не противоречит MVC, а хранение данных представления или контроллера в модели — противоречит. Единственное хранилище данных в MVC-приложении означает, что у представления нет своих данных, в примере же данные у представления есть, собственно только они и есть, нет даже поля типа isAuthenticated, которое можно было бы отнести к модели.
Спасибо, статья хорошая, только она как раз для новичков, которые только знакомятся с redux. Поэтому, уж извините, я вставлю свои пару копеек ;) Есть пара серьезных замечаний:

1. Не называйте это MVC. Во-первых, то что, вы показали — это ближе к MVVM, во-вторых, не пытайтесь подогнать redux под какие-то другие термины. Не ставьте себя в рамки. Redux — это redux, определенная архитектура приложения со своими особенностями и best practices

2. Может сложится впечатление, что state — это что-то плохое. Это не так! Использовать state можно и нужно, если этот state связан только с внутренним состоянием компонента, а не с состоянием приложения. Например, можно кешировать сложные вычисления уровня представления (смещения, цвет и т.д.). Не стоит загаживать хранилище такими вещами, иначе потом там сам черт ногу сломит.

3.
componentWillMount() {
this.props.tryAutoFill();
}

Не делайте так! Вы сами себе противоречите. Компонент не должен знать про автозаполнение. Одной строкой вы сделали из «глупого» компонента «умный». Разделение должно происходить на уровне логики, а не паттернов (callbacks, props). По факту ваш компонент теперь «знает» про redux (точнее про какую-то логику, которую нужно дернуть, чтобы себя заполнить).

4. В «умных» компонентах нет ничего плохого, при правильном их использовании. Ваш react-redux код по факту и есть «умный» компонент.
  1. Ставить рамки это хорошо. Чем жестче ограничения, тем однообразнее будет код.


  2. Да — я думаю, что state внутри компонента это плохая идея, и стоит избегать его использования. Единственный случай, когда это правда необходимо — это разработка компонента, который может работать в обоих, в Stateful и Stateless, режимах. Классический пример это React-овский Input. Он умеет Controlled/Uncontrolled режимы. И ведет себя сильно по-разному.


  3. tryAutoFill ничем не отличается от updateLogin. Т.е. Компонент знает, что какие-то callback-и будут, знает интерфейс, но не имеет представления о реализации


  4. "Умные" компоненты появляются только, когда лень реализовывать правильно.
    Не стоит загаживать хранилище такими вещами, иначе потом там сам черт ногу сломит.

Неужели вы ни разу не упирались в то, что с каким-то Stateful-компонентом со временным State не удается реализовать нужный сценарий, и приходится много переписывать?
Вас Undo/Redo не беспокоит? Или серверный рендеринг?
Проблемы не надуманы — они реально существуют

1. Вопрос философский. По моему скромному мнению разработка — это поиск гармонии, и крайности только мешают в этом деле.

2. Внутренняя кухня компонента должна оставаться внутренней кухней. Наверх выносятся только те callback и свойства, которые могут быть интересны внешним компонентам. Но тут очень важно чувствовать границу. Приходит с опытом.

3. >> Т.е. Компонент знает, что какие-то callback-и будут, знает интерфейс

У вас не вызывает диссонанс фразы «глупый компонент» и «компонент знает»? Это неверно. Компонент предоставляет (!) интерфейс для внешних компонентов. Единственное, о чем знает глупый компонент — это свое внутреннее устройство.

Он принимает свойства и вызывает callback'и при изменение СВОЕГО состояние. Есть очень простое правило: если вызов callback'а отвечает на вопрос «почему?» — все ок, а если на вопрос «зачем?» — что-то не так. Поэтому, кстати, и принято у callback'ов делать префикс «on». Посмотрите на реактовские onChange, onClick и т.д — такая маленькая подсказка.

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

В вашем случае вызов tryAutoFill() — это знание о внешней логике. Этот вызов должен делать кто-то более «умный», а в «глупый» компонент должны уйти уже нужные значения.

4. Поймите, у вас тоже есть умный компонент, только вы назвали его Controller.
Согласен почти со всем, но насчет локального state мне кажется, что ограничения можно сделать полегче. В частности, использовать state только тогда, когда данные в нем нужны только самому этому компоненту и его дочерним элементам. Если они становятся нужны где-то еще, то вынести их в основной store. Простой пример: в компоненте для вкладок номер текущей вкладки хранить в state, но если потребуется переключать вкладку извне, то вынести это в store. Весь рефакторинг будет заключаться в замене setState на dispatch и передаче добавленных в store данных по цепочке компонентов через props. Это может занять немного времени, но по-крайней мере это прямолинейная, рутинная работа, над которой не нужно думать (может быть можно даже сделать плагин для IDE, который будет делать это сам). А вот через ref такие проблемы решать — на самом деле антипаттерн.
НЛО прилетело и опубликовало эту надпись здесь
А можно описать пример посложнее? Например, как с помощью этой связки сделать таблицу с постраничной или бесконечной загрузкой? Сложность даже не в stateless-компонентах, а в том, как хранить модель (а точнее коллекцию моделей).
Ну теперь я по крайней мере точно вижу что это не MVC, т.к. модель хранит «filteringMode», т.е. параметр отображения. Ещё впечатляет огромный объём кода для такого простенького приложения. Но того о чём я спрашивал там нет и близко. Как быть если список todos придётся загружать частично, а потом подгружать по мере необходимости? А когда пользователь изменит настройки фильтрации/сортировки? А если их (элементы списка) спользуют другие представления (компоненты)?
НЛО прилетело и опубликовало эту надпись здесь

Насчет stateless и lifecycle: тут весьма неоднозначно. Сам компонент не знает в какой стадии жизненного цикла находится — из функции render и из других кастомных функций нельзя узнать в какой стадии жизненного цикла сейчас компонент.

НЛО прилетело и опубликовало эту надпись здесь
А как в этом MVC логику положить в модель? Это больше на какой-то VC похоже.
Зарегистрируйтесь на Хабре , чтобы оставить комментарий