Пожалуйста, опишите ошибку

Нашли баг? Помогите нам его исправить, заполнив эту форму

Как использовать Redux в библиотеке для создания web-компонентов Polymer

Кирилл Торгашин
Web-разработчик

Приложения на React принято создавать по принципу единого хранилища состояния. Обычно само состояние хранится в корне приложения, что дает возможность оповещать все ветки приложения, об изменении этого самого состояния.

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

Flux — это идея, имеющая следующую реализацию: необходимо отделить состояние от самого приложения и обеспечивать общение компонентов следующим образом: изменения компонента (actions) вызывают диспетчер (dispatcher), который оповещает состояние об изменениях, состояние (store) в свою очередь оповещает компоненты о том, что пора что-то менять. Важную часть в этой последовательности занимает однонаправленный поток данных.

Одной из реализаций flux-идеи стал Redux. Redux позволяет компонентам общаться с хранилищем напрямую. И набирая популярность, redux активно выходит за пределы react-библиотеки.

В статье я расскажу про то, как использовать redux в библиотеке для создания web-компонентов Polymer. Для демонстрации я создам простое приложение, которое считает количество кликов по компоненту и меняет его заливку после каждого нажатия.

Первым делом устанавливаются зависимости, необходимые для работы:

bower i --save Polymer
bower i --save tur-nr/polymer-redux
npm i --save redux

Создание приложения начинается с инициализации хранилища состояния (redux store).

const initialState = {
       clickCount: [], 
       colors: ['red', 'purple'],
       color: 'blue'
}

Следующим шагом станет определение функции, позволяющей нам менять состояние приложения (reducer), которая будет принимать в качестве аргументов текущее состояние и тип действия. Так как приложение считает количество нажатий по компоненту, то можно обойтись в одно действие:

const reducer = (state, action) => {
        if (!state) return initialState;
        switch (action.type) {
            case "CHANGE_COMPONENT": 
                state.color = state.colors.pop();
                state.colors = [action.color, ...state.colors];
                return Object.assign({}, state, { clickCount: state.clickCount + 1 });
        }
    };
const store = Redux.createStore(reducer);
const ReduxBehavior = PolymerRedux(store);

В javascript есть определённые особенности поведения сущностей. В частности, переменные типа Number при присваивании получают копию значения исходной переменной. А при присваивании новой переменной значения другой переменной, имеющей тип object, интерпретатор отдаёт уже ссылку на этот объект в памяти, что позволяет менять свойство объекта из любой переменной. Эта особенность поведения объектов называется «мутацией», и чтобы её обходить и всегда возвращать новые значения в состояние, необходимо использовать методы, создающие новые сущности.

Заключительным шагом становится объявление хранилища.

Создание компонента. Обязательными условиями для работы redux является импорт его в компонент, где необходимо следить за состоянием приложения.

<link rel="import" href="../bower_components/polymer/polymer.html">
<link rel="import" href="../bower_components/paper-ripple/paper-ripple.html">
<link rel="import" href="../redux-store.html">
<link rel="stylesheet" href="../style.css">
<dom-module id="colored-component">
    <template>
        <div class$="big-square [[color]]" on-tap="changeComponent" onselectstart="return false">
            <h3>You crossed the [[clickCount]] clicks!</h3>
            <paper-ripple></paper-ripple>
        </div>
    </template>
    <script>
      Polymer({
        is: 'colored-component',
       
        behaviors: [ ReduxBehavior ],
        properties: {
          clickCount: {
            type: Number,
            statePath: 'clickCount'
          },
          color: {
            type: String,
            statePath: 'color'
          }
        },
        actions: {
          change:function(color) {
            return {
              type: 'CHANGE_COMPONENT',
              color: color
            }
          }
        },
        changeComponent: function() {
            this.dispatch('change', this.color);
        }
      });
    </script>
</dom-module>

Углубляться в синтаксис работы с Polymer я не стану, для этого есть статья Алексея Танкаяна (Просто и понятно о Polymer), но хочу отметить, что в поведениях (behaviors) компонента нужно указать ReduxBehavior, который я объявил в компоненте create-store.html, и в каждом свойстве (properties) нужно указать его путь в хранилище (statePath: 'clickCount'), и создать действие (в действии следует указать тип, для того, чтобы redux смог определить, что именно мы от него хотим), которое через диспетчер нужно передавать в хранилище.

Придав немного стиля, получаем такой вот прекрасный счетчик:

Немного демонстрации его работы. Первым делом инициализируется состояние, так как при запуске у нас нет никакого состояния, то передается initialState.

После чего устанавливается цвет фона, полученный из initialState и количество сделанных кликов.

После клика на компонент вызывается action, который ищется в reducer’е и в дальнейшем.

В state происходят изменения, о которых он уведомляет view и происходят изменения!

Вот таким нехитрым способом можно использовать знаменитую библиотеку Дэна Абрамова в Polymer! Счетчик, представленный выше, прост в понимании и дает представление о работе redux и его взаимодействии с библиотекой от Google. Более детальную информацию о тонкостях работы с Polymer можно найти тут: https://www.polymer-project.org/1.0/docs/devguide/feature-overview.

На этом все. Happy coding!

Читать и комментировать
Работа с JSON в MySQL

Максим Лимонов

9 января

Работа с JSON в MySQL

Подходы к реализации поиска документов на сервере
Интернационализация приложения на React
Skobbler maps

Марк Левковский

4 августа 2016

Skobbler maps

Краснодар

Коммунаров, 268,
3 эт, офисы 705, 707

+7 (861) 200 27 34

Хьюстон

3523 Brinton trails Ln Katy

+1 833 933 0204

Москва

+7 (495) 145-01-05