Polymer Redux Support

Polymer Redux (updated)

Polymer Redux is a powerfull integration between Polymer and Redux.

Polymerize supports Polymer Redux and let you use it from dart in a seamless way.

This example will show how to build a simple component updating a list of elements using polymer-redux.

About polymer-redux

Polymerize is using a modified version of polymer-redux in order to be more compliant with the dart language.

The original version had two problems.

  1. it defines a function that will generate a mixin from a redux store.
  2. the store instance is bound to the mixin and it is unique for the application

The first point is a problem because in dart is not possibile to define a class dynamically.

The second doesn’t allow for encapsulating a redux component inside another redux component easily.

Model, actions, reducer

Let’s start for the model. The model is our immutable data structure. In our example our model will consist in a List of Item objects:

class SampleModel {
  final List<Item> items;
  SampleModel({this.items = const []});
}

class Item {
  final String label;
  Item({this.label});
}

In order to update the model we have to define some actions and a reducer.

A reducer is nothing else than a function that produces a new version of the model given the previous version and an action.

An action rappresent a specific change that we want to apply to the model: an object that conveys all the information needed for the reducer to do a specific change on the model.

Let’s start by defining two different actions to updated our model: one action to add a new Item to the items list and another one to remove an Item from the list of items:

static addItemAction(Item newItem) =>
  new ReduxAction<Item>(type:'ADD_ITEM',detail: newItem);

static removeItemAction(int index) =>
  new ReduxAction<int>(type:'REMOVE_ITEM',detail: index);

For commodity we have defined two static methods that will create the actual ReduxAction we need. A ReduxAction is an object with a type and a detail. The type should be an identifier that uniquely identifies a change that we want to apply to the model, while the detail is the payload we need in order to accomplish the change (the item we want to add or the index we want to remove in the example).

And here it comes the reducer for our model and actions :

The main reducer function is :

SampleModel myReducer(SampleModel model, ReduxAction action) =>
  ((model) => new SampleModel(items:_reduceTodoList(model,action)))
  (model??new SampleModel());

while for the todo list we have :

List<Item> _reduceTodoList(List<Item> items, ReduxAction action) {
  switch (action.type) {
    case 'ADD_ITEM':
      return _addItem(items,action.detail);
    case 'REMOVE_ITEM':
      return _removeItem(items,action.detail);
  }
}

List<Item> _addItem(List<Item> items, Item newItem) => []
  ..addAll(items)
  ..add(item);

List<Item> _removeItem(items,int index) =>
  new List.from(items)
   ..removeAt(index);

These methods basically generates a new version of the model given the previous version and the action. For example when adding a todo a new model is generated that has a list that is the same of the previous with that element added.

The Store and the component

Now its time to put the things together and define our redux-enabled component.

Let’s start be declaring a global store instance then we will define the mixin that every components that want to share the same model will have to use:

final myStore = createStore(myReducer);

@PolymerBehavior('MyReduxMixin')
abstract class MyReduxMixin implements
  ReduxLocalBehavior,
  DartCallbacksBehavior {

  readyPostHook() {
    this.store = myStore;
  }

};

The mixin must “extend” the ReduxLocalBehavior and DartCallbacksBehavior. The first is needed to implement a redux behavior, the latter in order to use callbacks in dart without having to worry about calling the super implementation.

In the readyPostHook we set the store property (that comes with ReduxLocalBehavior) so that every component using MyReduxMixin will get the store automatically set.

Please note that defining a new behavior is an optional step (you could use ReduxLocalBehavior directly, and set the store in many different ways). But is a good practice to encapsulate that logic in a single behavior.

Finally we can define our component :

@PolymerRegister('my-redux-comp')
abstract class MyReduxComponent extends PolymerElement
 implements MyReduxMixin {

   static get template => """
<dom-repeat items='[[myItemList]]'>
 <template>
    <paper-button on-click='removeMe'>[[item.label]]</paper-button>
 </template>
</dom-repeat>
<paper-button on-click='addMyItem'>Add one</paper-button>
""";

   @Property(storePath:'items')
   List myItemList;


   static addItemAction(Item newItem) =>
     new ReduxAction<Item>(type:'ADD_ITEM',detail: newItem);


   static removeItemAction(int index) =>
     new ReduxAction<int>(type:'REMOVE_ITEM',detail: index);

   addMyItem(Event ev,detail) =>
    dispatch(addItemAction(new Item(label:'new item')));

   removeMe(Event ev,detail) =>
     dispatch(removeItemAction(
       shadowRoot.querySelector('dom-repeat').indexForElement(ev.target)));


 }

Let’s examine it with attention. First of all, in order to have access to the store the component just have to mixin the newly defined redux mixin MyReduxMixin.

To bind component properties to paths in the store just use @Property annotation with the storePath fields.

To dispatch an action use the dispatch method (that comes with the ReduxLocalBehavior).

When the user clicks on the “Add one” button the addMyItem method will be called that will create and dispatch the ADD_ITEM action to the store. Then the reducer will be used to produce a new version of the model and the properties bound to the store will be updated.

When the user clicks on the “Remove me” similarly will happen for the REMOVE_ITEM action and the item gets removed.

Conclusions

Polymer with Redux rocks. And with Dart rocks twice ! Enjoy.