Javascript-форум (https://javascript.ru/forum/)
-   Оффтопик (https://javascript.ru/forum/offtopic/)
-   -   React'а тред (https://javascript.ru/forum/offtopic/53890-react%27-tred.html)

Gozar 28.02.2015 19:58

Цитата:

Сообщение от l-liava-l
Википедия:
Цитата:
Шаблонизатор (в web) — это программное обеспечение, позволяющее использовать html-шаблоны для генерации конечных html-страниц.
Реакт предназначен именно для этого, а его возможности, недостатки и архитектура не имеют значения в данном случае.

Оно не конечный html страниц, оно виртуальный DOM, который рендерится в HTML. Оно не HTML, оно имеет XML ситнаксис, как минимум и оно имеет свой шаблонизатор.

Цитата:

Сообщение от l-liava-l
Ангуляр это целая система в которой тож есть свой шаблонизатор.

Это лицемерие. У реакта тоже есть шаблонизатор и называется он тадам JSX, а ещё есть архитектурное решение ака flux, а ещё есть аддоны, которые тоже часть системы. Не хочешь не подключай, ну дык я думаю в ангуляре также.

Будь здоров, кушай торт, он полезен :)

Gozar 07.03.2015 16:37

Есть две архитектуры flux(store, action, component, dispatcher). Одна зависима от другой. Это значит что пока 1 не обновиться, 2 не должен обновляться.
Как организовать внутри одного диспетчера я понимаю(ставим waitFor), а между двумя как построить взаимосвязь недогоняю.

Как организовать эту зависимость с диспетчерами?

melky 07.03.2015 18:11

Цитата:

Сообщение от Gozar (Сообщение 360043)
Есть две архитектуры flux(store, action, component, dispatcher). Одна зависима от другой. Это значит что пока 1 не обновиться, 2 не должен обновляться.
Как организовать внутри одного диспетчера я понимаю(ставим waitFor), а между двумя как построить взаимосвязь недогоняю.

Как организовать эту зависимость с диспетчерами?

Добро пожаловать на грабли событийного программирования! С моделями было проще, да?)

Flux должен быть один. Dispatcher - тоже. http://fluxxor.com/images/flux-complex.png

Хочу заметить, я перепробовал все способы и остановился на одном, как самом удобном. Он выделен красным. Почему он - расскажу в конце.

1. Асинхронные действия. Из action возвращаешь Promise. Когда он выполнится, выполняешь другое действие
2. waitFor. Это, можно сказать, сахар над Store.on('change'). Все просто - в хранилище A выполняешь waitFor('B', callback). Когда вызовится B.emit('change'), выполнится и переданный callback
3. Недокументированный API. Хранилища наследуются от EventEmitter. У него есть привычный всем метод once (колбек на один раз).
4. componentDidUpdate(prevProps, prevState) внутри Controller Component. Проверяем, изменились ли данные в новом состоянии. Если да - отсылаем action.

Теперь обо всём по порядку.

1. Асинхронные действия

добавляем в actions параметр callback, или возвращаем Promise.

Это идет в разрез с идеей Flux. Это не стоит использовать (имхо)

2. waitFor

waitFor мне неплохо вмазал (шо аж день не отпускало), так что, думаю, следует хорошо пояснить этот момент

Я использовал Fluxxor, так что его и буду описывать. Документация по Store.waitFor

Как вы видите, waitFor должен находиться внутри хранилища. Как тогда выполнить обновление с зависимостями?

A - master Store
B - slave Store


Сналача, регистрируем обработчик изменения через вызов нужного action (напр. Flux.actions.b.waitForAUpdate)
Далее, выполняем изменение хранилища A (напр. Flux.actions.a.setItem)

И приходим к проблеме. Вызов этого дела выглядит так:
Flux.actions.b.waitForAUpdate()
Flux.actions.a.setItem('yolo')


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

тут либо можно изгальнутся костылем, пустив обновление состояния A через таймаут:
Flux.actions.b.waitForAUpdate()
setTimeout( () => Flux.actions.a.setItem('yolo') )


либо вернуть из создателя действия (ActionCreator, лол) обещание, которое содержит в себе таймаут:
Flux.actions.b.waitForAUpdate().then( () =>
  Flux.actions.a.setItem('yolo')
)


Исходник для Flux.actions.b.waitForAUpdate получается таким:

Файл flux/actions/b.js:
export default {
  waitForAUpdate() {
    return Promise.delay().then(() => 
      this.dispatch('WAIT_FOR_A_STORE')
    )
  },
}

(Promise.delay - это нестандартный метод, из библиотеки bluebird)

Для сравнения, создатель действия без обещания - такой:
Файл flux/actions/b.js:
export default {
  waitForAUpdate() {
    this.dispatch('WAIT_FOR_A_STORE')
    // и чё, кода действие выполнится то?
  },
}


Если у вас крупное приложение, и действия отсылаются через таймауты, то у вас появятся проблемы, если вы будете использовать этот способ.

3. Недокументированный API.

У нас ведь Flux, так? значит, все действия и хранилища видны всему.

Вкратце - одноразово подписываемся на событие изменения, и из него уже дальше пляшем
Flux.stores('A').once('change', () => {
  // хранилище изменило состояние
  const AState = Flux.stores('A').getState()
  Flux.actions.b.doSimethingWith(AState)
})

Flux.actions.a.setItem('yolo')


Наверное, это дело лучше красиво спрятать в ActionCreator, обернув обещанием.

4. componentDidUpdate(prevProps, prevState)

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

Состояние хранилищ передается controller component, который расфасовывает полученные данные вниз по дереву компонентов через свойства (props)

Fluxxor предоставляет сахарок для легкого подписывания на событие изменения хранилищ и перевод их состояний в состояние компонента.

componentDidUpdate, как вы знаете, вызывается после того, как у компонента сменились свойства или состояние. Ну и измениться они могут, в случае controller component, из-за смены состояния одного из прослушиваемых хранилищ

выглядит это так:

A - master Store
B - slave Store


Пусть компонент копирует состояния хранилищ в пространства имен :
// это - слушалка события 'change' у двух наших хранилищ
onSomeStoreChanged() {
  const A = Flux.stores('A').getState()
  const B = Flux.stores('B').getState()

  return { A, B }
}

// состояние хранилища A будет в this.state.A
// для B  - соотв.


отсылаем данные хранилищу A:
Flux.actions.A.setItem('yolo')


внутри CDU, если что-то изменилось, и это состояние хранилища А, то нужно обновить данные в В:
componentDidUpdate(prevProps, prevState) {
  if (this.state.A !== prevState.A) {
    Flux.actions.b.updateFromItem(this.state.A.item)
  }
}


ну, или можно использовать CWU (цепочка реакций должна пойти быстрее):
componentWillUpdate(nextProps, nextState) {
  if (this.state.A !== nextState.A) {
    Flux.actions.b.updateFromItem(nextState.A.item)
  }
}


А у нас цепочка зависимостей (A -> B -> C)? Тогда:
componentWillUpdate(nextProps, nextState) {
  if (this.state.A !== nextState.A) {
    Flux.actions.b.updateFromItem(nextState.A.item)
  }
  if (this.state.B !== nextState.B) {
    Flux.actions.c.updateFromItem(nextState.B.item)
  }
}


Как-то так ... в общем, у нас есть перенос логики обновления в компоненты-контроллеры, но для Flux - это нормально (вроде)

Почему лучше использовать CWU/CDU для цепочки событий? Все просто - действие (action), на момент выполнения этих методов, уже прошло круг от создателя действий до компонентов (напомню, только одно действие может обходить кружок в момент времени).

waitFor обеспечивает такой же функционал (вызывается, когда хранилище заявило об изменении состояния), но как вызвать действие (Action) внутри хранилища (Store)? МБ дело вкуса, но я лучше не буду пихать всякие actions.yolo() внутри чистенького Store.

Ну и еще - разделение логики обновления данных от самого обновления данных

И еще - абстрагирование от того, сколько действие выполняется. Получается действительно реактивная система обмена данными

Всё вышесказанное является огромнейшим ИМХО. Если есть предложения, буду рад их услышать.

melky 07.03.2015 18:45

Ещё пометочка, но отдельным сообщением.

1. Для сравнений типа this.state.B !== nextState.B нужно, чтобы данные не были мутабельными (иммутабельность, короче)

Это делается либо через _.cloneDeep, либо использованием билиотек, предоставляющих иммутабельные структуры данных

Можно и проверять на предмет изменения объекта через _.isEqual(this.state.B, nextState.B), но спор иммутабельность vs мутабельность - отдельная тема

2. Controller component - шаблон проектирования компонентов. Заключается в том, чтобы выделить 2 компонента, которые по одиночке используют один Store и поставить над ними родителя, который содержит в себе получение данных из Flux и её передачу по дереву компонентов с помощью props.

что-то типа такого (но тут не один Store, а два): http://2.bp.blogspot.com/-mSMHB7w43l...30.05%2BPM.png

count_control и sold_flag_control берется из Flux и передается компонентам через props с именами Sold flag и Count

melky 11.03.2015 11:57

React v0.13

Gozar 11.03.2015 14:24

Цитата:

Сообщение от melky
React v0.13

Не пинали бы меня со сроками запуска, начал бы переезд :)

Gozar 20.04.2015 23:03

Накой было резать html autocomplete?

nerv_ 01.05.2015 22:36

Поскольку ангуляр достал своим DI, тормозами и прочим, решил ознакомиться с реактом. Вчера посмотрел видео, сегодня почитал доку и написал первый пример на es6 + React.
//Test.jsx

class Test extends React.Component {

    render() {
        return (
            <h1>test</h1>
        );
    }

}

export default Test;

//client.js

import Test from './components/Test.jsx';

React.constructAndRenderComponent(
    Test,
    {},
    document.getElementById('container')
);

<!DOCTYPE html>
<html>
<head>
    <title>React Sandbox</title>
    <meta charset="utf-8">
    <script src="lib/react.js"></script>
</head>
<body>
    <div id="container"></div>
    <script src="dist/sandbox.js"></script>
</body>
</html>


результат https://yadi.sk/i/hrZgXP64gNiUf

С документацией по 0.13.2, конечно, беда.

Хочется, как в шаблонизаторе лепить ифы и фор_ичи, но нельзя :-E
Как же быть?
Смотрел в сторону react-templates, но не уверен.

Цитата:

Сообщение от l-liava-l
Ангуляр это целая система в которой тож есть свой шаблонизатор.

спрашивается, на кой такая система сдалась:
1. DI ангуляра не нужен, т.к. есть es6 модули
2. утилиты (forEach, isObject, etc) мне тоже не сдались, у меня свои
3. транспорт ангуляра (ajax) не требуется, у меня свой
Т.о., если модели у меня свои, утилиты свои, свой транспорт, но ангуляр заставляет меня юзать его собственные и при этом тормозит... не хорошо это :)
Да, с ним можно писать меньше кода. Декларативность нравится, но стоит ли оно того?
По большому счету, теперь мне нужна только вьюха.

Gozar 01.05.2015 23:19

Цитата:

Сообщение от nerv_
С документацией по 0.13.2, конечно, беда.

Там минимальные отличия от 0.12. Читай 0.12, отличия сами дойдут.

Цитата:

Сообщение от nerv_
посмотрел видео

Это видео, ещё та жесть. Смотри видео вконтактике в группе https://vk.com/reactjs и на eggheads


Цитата:

Сообщение от nerv_
Хочется, как в шаблонизаторе лепить ифы и фор_ичи, но нельзя
Как же быть?

Почему нельзя? Ещё как можно и нужно :)
Там сборка идет в рендере перед return

render: function () {
        var i = 0;
        var imgs = this.props.list.length == 0 ? '' : this.props.list.map(function(item){

                return <Photos
                            src={item.img}
                            title={item.name}
                            key={'photo-' + (i++)}
                        />;
            });
	    return (
            <div className="imgs">
            {imgs}
            </div>
	    );
}

Gozar 01.05.2015 23:21

Причем <Photos может состоять ещё из десятка компонентов или из одного <img>, это реально круто, наследование в html :)


Часовой пояс GMT +3, время: 21:13.