Pull to refresh

Comments 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 не нужно никаких надстроек, не нужно своего кастомного кода — нужно лишь придерживаться пары простых принципов, как раз для хорошей организации кода

UFO just landed and posted this here
Если уж приложение растет, никто не запрещает сделать 20 редьюсеров. А так же, использование библиотек вроде normalizr и reselect подразумевается априори.
UFO just landed and posted this here

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

UFO just landed and posted this here

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

UFO just landed and posted this here

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

При использовании 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 такие проблемы решать — на самом деле антипаттерн.
UFO just landed and posted this here
А можно описать пример посложнее? Например, как с помощью этой связки сделать таблицу с постраничной или бесконечной загрузкой? Сложность даже не в stateless-компонентах, а в том, как хранить модель (а точнее коллекцию моделей).
Ну теперь я по крайней мере точно вижу что это не MVC, т.к. модель хранит «filteringMode», т.е. параметр отображения. Ещё впечатляет огромный объём кода для такого простенького приложения. Но того о чём я спрашивал там нет и близко. Как быть если список todos придётся загружать частично, а потом подгружать по мере необходимости? А когда пользователь изменит настройки фильтрации/сортировки? А если их (элементы списка) спользуют другие представления (компоненты)?
UFO just landed and posted this here

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

UFO just landed and posted this here
А как в этом MVC логику положить в модель? Это больше на какой-то VC похоже.
Sign up to leave a comment.