Pull to refresh

Comments 35

Спасибо, все структурировалось в моей голове.

Вся соль, в функции Render и в том, что state — это не часть компонента, как мне кажется. Вот именно это место и полностью мне объяснило всю соль flux. Еще раз спасибо.
Но, насколько я понимаю, выносим за пределы компонента только то состояние, те данные, которые необходимо изменять на сервере.

И да, PS в статье я прочитал. :)
state все таки относится именно к компоненту, и именно state однозначно определяет как будет отрисован компонент.
За пределы мы выносим не state, а данные, и в нужный момент просто копируем эти данные(из store) в state
Да, запутался я в терминах. И данные и state под одну гребенку запихал. Перечитал свой коммент, понял, что написано не то, что я хотел сказать. И да, Вы абсолютно правы.
нет, я хотел сказать, именно, state. props это список параметров пришедших в компонент из вне(от родителя), а state набор параметров описывающий именно внутренее состояние компоненета
А вот и зря вы используете state. Flux создан для предотвращения непонятных изменений данных, и именно благодаря props мы можем декларативно объявлять компоненты и обновлять их основываясь на данных которые хранятся ВНЕ компонента. Тем самым имя куда более предсказуемые и чистые функции, нежели с каким-то магическим внутренним состоянием
state должен использоваться на контрольной вьюхе, а ниже спускаться только за счет пропсов. Хотя возможно вы просто запутали понятия вью, компонент и контрол-вью))
Кстати, я ошибаюсь, или подход с тем, что мы не следим за тем, как именно изменился State возможен только в ReactJS, за счет виртуального DOM'a?

Точнее можно везде делать полную перерисовку, но только ReactJS позволяет это делать без просадки рендеринга.
Скорее так, React полностью «перерисовывает» только виртуальный DOM, а реальный DOM трогаем по минимуму.

Выигрыш в скорости за счет того, что и новую версию виртуального DOM он сравнивает не реальным, а с предыдущей версией виртуального.
Да, я понимаю. Я и хотел сказать, что только в ReactJs, на текущий момент, можно делать реализацию, которая не зависит от того, как именно изменился state. Точнее я хотел уточнить, только ReactJs позволяет так делать?
Да, комментарии стоящие.
Позволю себе перевести один из них.

Я работаю над Atlas в Facebook и использую React+Flux уже полтора года, так что могу ответить на некоторые вопросы:

— Как отправлять данные на сервер и обратно?

Отправка данных на сервер: Action Creator делает асинхронный запрос к серверу и, как только получен success или failure результат, посылает Action с соотвествующей информацией в Dispatcher, а затем обновляются и Store. Впрочем, Action Creator может послать Action не дожидаясь ответа от сервера, предполагая оптимистичный исход.

Получение данных от сервера: когда вы запрашиваете данные из Store, он может начать загружать данные в бекграунде и проинформировать вас, как только они получены. Store может быть синхронным, в этом случае getBlah() возвращает null если данные не были загружены, или асинхронным, тогда getBlah() возвращает Promise.

— Как управлять связью между компонентами не имеющими общего родителя?

Используйте Actions и Stores — они глобальны и могут использоваться любыми компонентами.
Ещё один стоящий комментарий, на этот раз из facebook/flux репозитория на github:

Пример чат-приложения я подготовил специально для конференции ForwardJS осенью 2014 года…

… мы хотели показать вызов серверного API из Action Creators, как это предпочитает делать Jing (автор Flux). Однако, внутри FB многие обычно делают запросы к серверу непосредственно из Store. Я считаю оба эти способа корректыми и не отдаю предпочтения какому либо из них.

Одно из преимуществ обращаться к серверу в Action Creator, а не в Store, это обработка ошибок.
Сложилось впечатление, что фейсбуковцы переизобрели велосипед. Посмотрите на подход рендеринга Ember.js или динамический рендеринг Joosy. Он делает абсолютно то же самое, только прозрачно для пользователя: bind и dispatch магически происходят внутри фреймворков.
Единственный принципиальный плюс в решении от FB — это рендеринг через React с виртуальным DOM, и тот под глубоким сомнением. Насколько реально широк тот кейс, когда данные меняются, но не меняется отображение? И реально ли такая оптимизация _заметна_ пользователю, или её можно заметить только профайлером?
Из-за декларативности компонент/шаблонов React выигрыш огромен. Вам не нужно определять все состояния компонента и переходы между ними. При увеличении сложности это значительно упрощает поддержку.
Чем шаблоны React декларативнее шаблонов Ember?

Чтобы не быть голословным, я взял пример кода "A Stateful Component" с официального сайта React и реализовал его на Ember: emberjs.jsbin.com/figobe/edit?js,output Как видите, всё то же самое, только на два года раньше, чем React. ;)

React я не пробовал, но полагаю, что двусторонний data-binding и самовычисляемые свойства (computed properties, аналог нативных `get` и `set`, но со встроенным кешированием и автоматическим пересчитыванием при изменении зависимых свойств) избавляют от необходимости писать массу вспомогательного кода, который приходится писать в React.

Я уже не говорю про то, что если вы выбрали React, вам нужно самому позаботиться о Model, Controller, Router, Adapter и т. д… Код, соответствующий указанным слоям, есть в каждом приложении, просто не везде он вынесен в самостоятельные сущности. В лучем случае, вы надергаете зоопарк разнообразных библиотек и будете жирно смазывать их glue code'ом, чтобы заставить их работать друг с другом. В худшем, будете изобретать велосипеды.
Посмотрел следующий пример с офсайта React:

  render: function() {
    var createItem = function(itemText) {
      return <li>{itemText}</li>;
    };
    return <ul>{this.props.items.map(createItem)}</ul>;
  }


В Ember это выглядело бы так:

<ul>
  {{#each item in items}}
    <li>{{item}}</li>
  {{/each}}
</ul>


Как говорится, без комментариев.

Я уже не говорю о том, что объявлять функцию внутри функции — это не очень хороший стиль программирования. Не ожидал такое увидеть на титульной странице флагманского open source продукта Facebook. :P
>>объявлять функцию внутри функции — это не очень хороший стиль программирования
вы точно про JS говорите?
Единственный разумный повод объявлять функцию внутри функции — это создать замыкание или ограничить scope. В данном случае не происходит ни того, ни другого.

Это:

  render: function() {
    var createItem = function(itemText) {
      return <li>{itemText}</li>;
    };
    return <ul>{this.props.items.map(createItem)}</ul>;
  }


следовало записать так:

  createItem: function(itemText) {
    return <li>{itemText}</li>;
  },
  render: function() {
    return <ul>{this.props.items.map(this.createItem)}</ul>;
  }


Я тут протестировал, разница в производительности — двукратная: jsperf.com/sibling-functions-vs-nested-functions

Не говоря уже о разнице в сложности структуры кода (и, соответственно, читаемости), а также наличии/отсутствии возможности юнит-тестирования функции createItem.
Ну как минимум замыканий достаточно для того, чтобы не говорить что объявлять функции внутри функций — не очень хороший стиль программирования.

Что же до того, как записан код в примере, предполагаю, что так сделано намеренно, ибо это позволяет держать под контролем (в поле зрения) доступные для рендера функции внутри одного скоупа, и не смешивать с остальными методами вьюшки, что улучшает «понимаемость»

А за тест спасибо, возьму на вооружение
Да, клево, выглядит короче. Но все портит магия эмбера. Когда пробрасываются контексты их родительских контроллеров\вьюх\роутеров, и понять откуда появилась конкретная переменная — нет никакой возможности. Даже если дебажить — цикл рендера эмбера превращает дебаг в ад.
Так этот современный эмбер уже вышел или нет? Так-то компоненты были и до 2й версии, и ими я хоть как-то мог осмысленно пользоваться среди ужаса и боли наследованных\перенаследованных скоупов
Единственное, чего еще нет, это возможности из route передавать управление сразу в component, минуя view и controller. Эта возможность запланирована на версию 1.12 (недавно вышли 1.10 и 1.11beta).

На данный момент для каждого route создается по view и controller. Но о них можно спокойно забыть и передавать данные сразу в компоненты через шаблон.
объявлять функцию внутри функции — это не очень хороший стиль программирования

Можно поподробней? Например:

def joinPaths(*args):
    
    def preparePathPart(part): # prepare path parts to be joined by join method
         # trim slashes from the end
         # etc

    def joinParts(paths, delimiter='/'): # another function inside of a function, join works only with prepared data
         return args.join(delimiter)

    return joinParts( map(args, preparePathPart) )

>>> joinPaths('/etc/', 'init.d/', 'some.conf')
'/etc/init.d/some.conf'


P.S. это просто синтетический пример на python-like псевдо-языке
Я не понимаю, какие конкретно могуть быть проблемы с тем, что я тут использовал аж 2 функции внутри joinPaths

Ровно то же самое я делаю не только на Python, но и на PHP/JS/Ruby/Go. Ни у меня, ни у моих коллег за долгие годы не было с этим никаких проблем. Более того, я считаю, что такая внутренняя декомпозиция хорошо структурирует функцию внутри самой функции. Неплохо было бы увидеть хорошее объяснение, почему это плохой стиль.
Кстати по коду может показаться, что вложенные ф-ии создаются при каждом запуске joinPaths, хотя в действительности этого не происходит (в случае с питоном), вложенные ф-ии уже «скомпилированы» до этого, к ним просто применяются разные «замкнутые» переменные, поэтому такой подход не должен повлиять на производительность (если кого это волнует).
Да, в JS видимо не так, зато можно сделать «замыкание», результат как бы тот же — приватные методы, но при этом не теряем производительности: jsperf, у меня оно показывает чуть лучше результат.
Хм. То есть React шлёт diff между предыдущим и новым виртуальным домом в реальный. А как часто проще сравнить виртуальный дом, чем напрямую поменять реальный. Почему было не сделать как в WPF, где есть INotifyCollectionChanged?
Вся суть React в декларативности шаблонов. Компонент при каждом изменении полностью перерисовывается (но виртуально), затем React вычисляет минимально дешевую мутацию состояний из А в Б. В императивном подходе все мутации состояний нужно определять руками. При увеличесии сложности компонента возрастает сложности определения всех состояний и переходов. Соответсвенно возрастает риск ошибки и сложность понимания.
Ваше хранилище будет реагировать на посланное событие:

и далее
ListStore.items.push( payload.newItem );

мне одному кажется, что тут хранилище никак не реагирует на сообщение, а как раз диспатчер?
т.е. разве не правильнее будет сделать как-то так:
ListStore.items_push( payload.newItem );

т.о. не диспатчер будет менять какое свойство в хранилище, а диспатчер будет посылать сообщение хранилищу, чтобы оно изменило себя.
Only those users with full accounts are able to leave comments. Log in, please.