Last week we learned that the various MVC frameworks all share a the same abstraction called the Component, and that the Component is a tiny MVC application wrapped in one construct. More and more frameworks are using Components all the way down, this week we learn how an application which only uses Components works.
I call this the Post-MVC Age.
If we built our entire front-end applications with Components instead of using MVC how would that work? In this post I want to show you how to do just that. We will also discover some of the weaknesses of the Component model regarding state.
Lets start at the beginning: given that our application is written with Components we must define the "first" Component. This Component represents the "entire" application. Of course this Component is composed out of other Components which will in turn be composed of Components.
The application will be Components all the way down.
Basically a Component based application is a Tree. A Tree of components. The 'first' Component can be viewed as the root Component: the starting point of the tree. Each sub Component is a node inside of that tree.
Lets imagine a simple Todo List application. The tree would look something like this:
TodoApplication ├── AddTodo │ ├── Input | ├── Button (add todo) ├── TodoList │ ├── Todo | │ ├── Toggle | │ ├── Button (trash) └── Filters ├── Filter (all) ├── Filter (complete) ├── Filter (active)
The TodoApplication is the root Component. All other Components are directly descendant of this Component.
The AddTodo Component is responsible for adding Todo's behind the scenes, it is an Input with a submit button.
The TodoList component renders the current todos, it does so by rendering each todo via the Todo Component using a loop. The Todo Component has a button to toggle the Complete status of the Todo, and a button to remove the todo completely.
The Filters component renders a list of 'filters' which the user can use to filter the todo's. There is a filter for showing all todos, a filter for showing all complete todos, and a filter for showing all active (non complete) todos.
The Tree and the State
But what about the state of the application. Where do the todos actually live? Somewhere an array of todos's must be kept as an internal state of a Component. If you look at the tree you might thinks it would be either the TodoApplication or the TodoList Component.
Lets investigate both options, however it is important to keep in mind that a Component can only communicate one level up or one level down in the tree. Remember a Component is an island which has very specific communication channels. If a Component wants to 'talk' to a sibling he must do so via the parent. If a Component wants to 'talk' to his grandchild he must do so via his own child Component.
The TodoApplication Component
Option A is in the TodoApplication:
If the TodoApplication Component holds the todos then they can then be passed down to the TodoList component. When a filter is clicked it reports it back to the TodoApplication Component, where the todos can immediately be filtered. When the AddTodo's submit button is clicked he communicates it back directly to the TodoApplication via an event. This event can then hold the text of the todo the user entered.
The TodoList Component
Option B is in the TodoList:
The TodoList Component can keep the actual reference to the todos. When a filter is clicked it reports it to the TodoApplication which routes the event to the TodoList. Same for the AddTodo Component when a todo is added: a call is made to the TodoApplication which routes it back to the TodoList.
Lots of little Channels, lots of little States
Given the two options which option would be the best? I think the answer may be neither solution is optimal.
The problem with both solutions is that the the state of the application as a whole is difficult to reason about. Because each Component internalizes a part of the state. Where does the active filter live? Where do the todos live? There are lots of little islands which have lots of little pieces of state.
In both solutions multiple events are routed through Components without them actually doing anything with them except to pass them on. This makes tracing events through the tree very difficult to reason about. There are too many channels producing too much noise.
Whilst each Component is still relatively easy to understand in isolation, the whole picture is blurred. The reason for this is that Components are talking to each other in a bi-directional manner.
In short information is flowing all over the place.
We need to talk about state
One of the downsides of the traditional MVC pattern is that state was difficult to manage, because state manipulation could happen in multiple places.
This downside also appears in the Component only model: state is difficult to manage, but not for the same reasons as in MVC. The reason is because there is communication between components which goes in both directions and sometimes through multiple components.
So whilst a Component itself is still easy to understand. The whole application is a complex web of communication. In the next weeks we will look into the various strategies that the community came up with to tackle this complexity.