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
- Redux - a brilliant architecture which simplifies state maintenance
- Polymer - a back-to-nature approach that puts elements back at the center of the universe
- Component model (custom element)
- Concept of data flow (attributes/properties and events)
- declarative syntax (HTML)
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
- polymer - random generated data
- Apple - aapl
- Google - goog
- Facebook - fb
- Shell - rdsa.as
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.