Reactless Redux

Redux’s own documentation is very clear in stating that the tool does not require any particular view library yet the basic example makes heavy use of the React and React-Redux bindings.

While there’s plenty of examples of usage with different frameworks, I wanted to see with my own eyes how the classic to-do app would look without the help from any UI library whatsoever. So I took it upon myself to remove any traces of React/ReactDOM and replace those bits with vanilla javascript.

The sample project has the Redux part left intact but all of the UI components where refactored. All markup has been extracted to an `index.html` in the form of HTML 5 template elements to leverage modern browser capabilities:

<form class="add-todo">
<input class="new-todo" />
<button type="submit">Add Todo</button>
</form>
<ul class="todo-list">
<template class="todo-item"><li>TODO_ITEM</li></template>
</ul>

The logic for each presentational component was left on the corresponding script file. Because these were originally pure stateless functional components we are able to re-write them as plain ES6 functions. For instance, in the case of `AddTodo` the following original JSX code:

const AddTodo = { dispatch } => {
let input
return (
<div>
<form onSubmit={e => {
e.preventDefault()
dispatch({type: 'ADD_TODO', text: input.value})
input.value = ''
}}>
<input ref={node => { input = node }} />
<button type="submit">Add Todo</button>
</form>
</div>
)
}

Becomes this regular function:

const renderAddTodo = el => {
var original = 'original' in el? el.original: el
var newEl = original.cloneNode(true)
newEl.original = original
// We work on a copy of the form element
newEl.addEventListener('submit', e => {
e.preventDefault()
const input = newEl.querySelector('.new-todo')
store.dispatch({type: 'ADD_TODO', text: input.value})
input.value = ''
})
el.parentNode.replaceChild(newEl, el)
}

The trick here involves always keeping a copy of the original element so we can re-render it completely every time the store notifies us of a change. The rest is standard DOM manipulation and dispatching of actions. Here’s how we access the state from the store:

import store from '../store'
const renderLink = (el, filter) => {
...
var active = filter == store.getState().visibilityFilter
...
}

Apart from a better understanding of what role a framework such as ReactJS would be fulfilling for us, this approach does have a few advantages, namely:

  • Less dependencies. Project only uses the tiny redux.js with the codebase taking up a similar amount of lines
  • Leverage of browser capabilities and optimizations. The browser already knows how to efficiently render DOM changes, specially with `template`
  • Separation of HTML for presentation and logic. No more confusing JSX syntax (<input ref={node => { input = node }} />)

Feel free to contact me and let me know what you think. In an upcoming article I will demonstrate how to substitute the only remaining dependency -Redux- with (you guessed it!) pure javascript.


Check out the companion project for this article at https://github.com/coderdiego/reactless-redux