Yet another simplified todo list.

Learning React Without Using React Part 1

First Concepts Then Frameworks

JavaScript Inside
Published in
5 min readFeb 9, 2016

--

There’s a lot of confusion when it comes to React. The following is a lightweight introduction into React and its underlying principles.

What will you learn when you are finished with part 1 and 2? A high level understanding of why you might need React and maybe Redux or another state container.

What you will not need: No need for JSX, ES6/ES*, Webpack, Hot Reloading, understanding of how the virtual DOM functions not even React itself.

First things first:

Let’s take a look at the TodoMVC code for jQuery.

You will notice there is a method render() which gets called every time an event is triggered or data is updated. Let’s build a naive example, where changing the value of an input, triggers a render function that updates the DOM element.

var state = {value: null};$('#input').on('keyup', function() {
state.value = $(this).val().trim();
render();
});
function render() {
$('#output').html(state.value);
}
render();

We’re keeping everything in sync by using a global state variable. This means that an input update does two things: 1. it updates the app state and 2. calls the render function. The render function updates the DOM according to the app state now.

Keep this example in mind. We will get back to it in a moment.

Here’s another idea to consider:

function output(text) { 
return '<div>' + text + '</div>';
}

Calling output(‘foo’) will return ‘<div>foo</div>’.

And now consider the following example:

function h2(text) { 
return '<h2>' + text + '</h2>';
}
function div(text) {
return '<div>' + text + '</div>';
}
function header(text) {
return div(h2(text));
}
console.log(header('foo') === '<div><h2>foo</h2></div>'); //true;

We’re writing functions that return a string based on an input. Calling header with the same argument will always return the same string. If you have ever wondered about stateless functions in React, well this is a simplified version. Stateless functions return a React element representation not a simple string. But you get the idea.

So we know that we can create functions that return strings now. Let’s get back to our original example and expand it to display an add button as well as an items list.

This is the visual output for now. We have a simple todo list including the ability to toggle an items state (active or finished).

Simpe todo list

A set of defined events updated the state and then called our render function. render then created the item list and added the string to the list element. We added state to simplify the interaction between events and elements. Instead of having to define every event and element and their respective relationship, we always update as soon as an action has been triggered. It simplifies handling complex interactions. We always call render when state has changed.

This works quite well already. We can add items to the list by entering a title via input box and we can toggle the items state by clicking on the item itself.

The render function looks quite messy. Let’s try to create a function that expects a input and returns a string based on that input.

function ItemRow(props) { 
var className = props.completed? ' item completed' : 'item';
return '<li class="' + className +'">' + props.text + '</li>';
}
function ItemsList(props) {
return '<ul>' + props.items.map(ItemRow).join('') + '</ul>';
}

We’ve cleaned up the render function.

function render() {
$('#list').html(ItemsList({items : state.items}));
}

What if render didn’t know about the state, and would expect an input instead? Well this is easy to achieve now, we can simply refactor the render method to expect props (this is what a React Component expects).

function render(props) {
$('#list').html(ItemsList({items : props.items}));
}

render doesn’t need to know about the external state. This enables us to simply call render with any given input. Which also means that re rendering the application will return the same results again and again. We should also keep in mind that the Dom is a side effect, but let’s neglect the fact for a moment.

By separating explicit state from the rendering part we can easily achieve Undo/Redo for example. Which means we are able to create a history and save the current state every time this changes.

Another optimization is to pass in the root node instead having to explicitly define the node inside the render function.

function render(props, node) {
node.html(ItemsList({items : props.items}));
}

So we can simply call render with a defined state and a root node now.

render(state, $('#list'));

But how can we re-render without having to explicitly call the render method after updating the state?

Let’s build a store that simply calls our render method as soon as the state has updated from anywhere in our application. Here’s a first try. While this implementation is very basic, it’s a good starting point for creating a more advanced state container.

function createStore(initialState) {
var _state = initialState || {}, _listeners = [];
function updateListeners(state) {
_listeners.forEach(function(listener) {
listener.cb(state);
});
}
return {
setState: function(state) {
_state = state;
updateListeners(state);
},
getState: function() {
return _state;
},

onUpdate: function(name, cb) {
_listeners.push({name: name, cb: cb});
}
};
}

And we can simply update the state via the store setState method. Our render function gets called as soon as the state changes.

var store = createStore(state);store.onUpdate('rootRender', function(state) {
render(state, $('#list'));
});

Here is a link to the current working example.

What have we seen now? We have seen the simple principle of one-way data flow. We pass in the state to the render function and the state trickles down the function hierarchy. ItemsList for example passes on the appropriate props to the ItemRow function. We have created components and we have composed those components to something even bigger. Remember the header example, where we composed div and h2 function into header. We’re dealing with pure functions here. This makes all our updates predictable. We also have a clear idea about our state now.

React does all this and more in a very efficient manner. Composition , optimized rendering by implementing the virtual DOM, unidirectional data flow etc.

…we can focus on examining React’s true strengths: composition, unidirectional data flow, freedom from DSLs, explicit mutation and static mental model.

Dan Abramov (https://medium.com/@dan_abramov/youre-missing-the-point-of-react-a20e34a51e1a)

There is still a lot more we can do, including building an improved state container, refactoring our listeners, implementing undo/redo and more nice features. All this in part 2.

Update: Part 2 is online.

This is work in progress. Any feedback welcome. I might be missing the point or might have neglected imported aspects. Leave a comment here or on twitter. Thanks.

Also read Fundamentals Then Frameworks.

--

--

A. Sharif
JavaScript Inside

Focusing on quality. Software Development. Product Management.