Polymer - Redux

Web Applications have changed over the years from being a simple static page to complex single page application (SPA). With the increase of complexity, the problems to solve are much harder too. If you get some random weirdness in your SPA, you can easily bug-hunt for days.

Luckily, there are two projects out there that aim at simplification

  1. Redux - a brilliant architecture which simplifies state maintenance
  2. Polymer - a back-to-nature approach that puts elements back at the center of the universe
Of course frameworks like React and Angular help you to build complex SPAs too, but why invent whole new concepts, while the DOM itself has a lot of framework type concepts just built into it:
  1. Component model (custom element)
  2. Concept of data flow (attributes/properties and events)
  3. declarative syntax (HTML)
Anyway, below I'll explain how I've used a single store, no two-way data-binding, actions and reducers in Polymer. You can visit the DEMO here and find the code on github

Demo site

For the purpose of this exercise I've written a Stock ticker application. As you can see in the demo you can search for ticker symbols. Here are some symbols to get you started

  1. polymer - random generated data
  2. Apple - aapl
  3. Google - goog
  4. Facebook - fb
  5. Shell - rdsa.as
At any time you can save the current state and hit reload without losing any data. The application will save a unique key to LocalStorage which is used to restore the state from Firebase With Redux you almost get this functionality for free :)

NOTE: If the demo doesn't work, make sure you're using an up2date browser, like Chrome Canary!!

Single source of truth

In Polymer everything is an element, for example, an AJAX call can be expressed in HTML. There is an element for everything.
So, in the main component of the demo app (stock-app.html) I connect the reducers with the store

<app-store id="store"
           state="{{state}}"
           reducer="[[reducer]]"></app-store>
I pass the reducers to the store and bind the state (child-to-host)
{{state}}
. From there on I pass state properties to child elements by one-way data-binding only.
<app-header hits="[[state.page.hit]]">
    ...
</app-header>

Actions

Through attributes components receive parts of the state. In order to change the state each component includes the store and can dispatches actions

<app-store id="store"></app-store>
...
this.$.store.dispatch('CHANGE_QUERYSTRING',
           {queryString: this.queryString});
However, everytime you include the store a new instance is created. So I've turned the app-store.html into a singleton.

Reducers

Reducers are pure function (wrapped in an element of course). They don't change the state object, but return a complete new state object

class QuoteReducer {
  transform(state, action, input) {
    switch (action) {
      case 'QUOTE_CHANGE':
        return Object.assign({}, state,
                 {quote: input.quote});
      default:
        return state;
    }
  }
}
I've tried to simplify the reducers even more by using Immutable.js, but that didn't work very well, because it breaks Polymer's data-binding.

So finally, when the reducers are ready, the store updates its state and Polymer makes sure that the changes flow (from top to bottom) through the application.

Conclusion

It turns out that these two great project work very well together. Unfortunately at the moment you need to do some additional work to create a Singleton and you cannot use Immutable.js.