The goals of this article are to define what stateful and stateless components are, otherwise known as smart and dumb - or container and presentational components. For the purposes of the article, we’ll be using Angular 2 Components to explain the stateful/stateless concepts. Bear in mind these concepts are not at all limited to Angular, and live in other libs/frameworks such as React.
Table of contents
- Impure versus Pure functions
- Stateful components
- Stateless components
- AngularJS 1.x version?
- Further reading
Before we begin, let’s clarify what “stateful” and “stateless” really mean in programming terms.
When something is “stateful”, it is a central point that stores information in memory about the app/component’s state. It also has the ability to change it. It is essentially a “living” thing that has knowledge of past, current and potential future state changes.
When something is “stateless”, it calculates its internal state but it never directly mutates it. This allows for complete referential transparency meaning that given the same inputs, it will always produce the same output. These are not essentially “living” as they are merely passed information. This means it has no knowledge of the past, current or future state changes.
Impure versus Pure functions
In my eyes, after reading this you’ll match up:
- Impure Function = Stateful Component
- Pure Function = Stateless Component
Check out my Pure versus Impure functions article for more depth, but we’ll cover the basics here.
Impure functions (stateful)
Let’s consider the following code from the above article, which parses the user’s
height values into Number types, then calculates the
bmi (Body Mass Index) based on the formula.
This is great in terms of the fact that it works, however this doesn’t create a reusable function that allows us to calculate the BMI elsewhere, test the formula easily, and relies on very procedural code. Let’s look at how we can make it “pure”. The important piece here is that this chunk of code is the driver of changes, and we can rely on pure functions to essentially create small isolated pieces of logic that accept data and return new data without relying on external variables.
Pure functions (stateless)
When we think about pure functions, we would be able to expect the same result every time, without the knowledge of lexical constant values such as
height. Let’s refactor the formula to be a pure function:
getBMI function can easily live elsewhere, not necessarily in the same function (as this example is inside the
onSubmit) function if you check the other article. Now the function is pure, it can be defined better in terms of “why”. The function has several attributes:
- It can be easily tested with mocked data
- It can be re-used multiple times to perform the role it has been given
- It has a defined input (function arguments)
- It has a defined output (
returnstatement with new data)
Here’s the thing: all four of the above concepts directly map across to thinking about stateless components.
Let’s now take the “impure” functions and look at the stateful component equivalent, followed by the “pure” functions that can be mapped across to stateless components.
Here are some attributes that a stateful component has:
- Drives state changes through functions
- Provides data (i.e. from http layers)
- May receive initial data via route resolves instead of service layer calls
- Has living knowledge of the current state
- Is informed by stateless components when something needs to change
- Can communicate with external dependencies (such as an http layer)
- Renders stateless (or even stateful) child components, perhaps within a single
<div>wrapper for layout containment
- Contain Redux actions (ngrx/store or ng2redux for example)
This list, and the one further in the article, was inspired by Dan Abramov’s Presentational and Container components article.
Stateful Todo component
In this article, we’re going to build a small todo application demonstrating these concepts, followed by their stateless counterparts.
First, let’s start off by rendering our base component, the
<app> to kick things off:
Inside here, we’re rendering a
<todos> component. This component will be stateful, let us continue! We’re not going to teach how to build a todo app, we all know this, so we’re going to look at how we can apply stateful and stateless paradigms to Angular 2 components and observe the ideas.
Let’s look at the component composition through ASCII art as we progress, so far we have an
Now onto the
You can see from the above that all we have is a container
<div> wrapping two further child (stateless) components. There is no other logic in the template other than that. The
<todo-form> component receives no input, but expects to bind an output called
onAdd. Next up, the
<todo-list> component receives the todos data from the
[todos] input binding, and two outputs
(onDelete), delegating the respective functions to the stateless counterparts.
The rest of the component class are the methods that make up the functionality of the todo component. Immutable operations are taking place inside each callback, and each callback is exposed to a stateless component so that it can run with it. All these functions are expecting is a notification that something has changed, for instance “Oh hey! Here’s a new todo label, go ahead and do your thing with it oh-mighty stateful component”. Note how the functions are only called from the child, stateless, level.
And that’s literally it on stateful. We cover some of the potential concepts that stateful components may contain. Let’s move onto the more frequently used stateless components.
TodoService represents the injected Service):
So what does this mean? Well, based on how function scope chains work, this means that stateless components have no knowledge of any part of the application they’re apart of. Which means they can be reused, easily tested and moved around very easily.
Here are some attributes that a stateless component has:
- Do not request/fetch data
- Are passed data via property binding
- Emit data via event callbacks
- Renders further stateless (or even stateful) components
- Can contain local UI state
- Are a small piece of a bigger picture
Stateless TodoForm component
Before we begin with this component, we need to understand that it’s a special kind of stateless component in the fact that it retrieves user input, and therefore contains UI state only:
This component also doesn’t receive any data via property binding, and that’s perfectly acceptable. The role this component plays is to capture the label of a new todo item, which is illustrated inside the
submit function. This is a special use case for stateless components that have a function inside to capture UI state and do something with it.
Stateless TodoList component
Let’s look at the second stateless component we have, a direct child of
@Output is well defined here, and as you can see, nothing else exists on this component class. We’re actually creating an
EventEmitter instance for each output, and also delegating this down into further stateless components, in this case the single
<todo> component, which will render each todo in our collection. We also delegate the
onDelete methods here, which are also bound to the parent, creating a basic chain. Let’s look inside
<todo> and we’re done:
Hopefully you can see a pattern emerging here! Again, we have some inputs and outputs that can send event information up to the parent, then up again (if needed). All of the above Angular 2 components are stateless. They have no knowledge of their surroundings, but are passed data via property bindings and emit changes via event callbacks.
Here’s the final ASCII render of the component tree that we’ve talked through:
AngularJS 1.x version?
Oh why not…
Full 1.x implementation
Here’s the full source code for the AngularJS 1.x version (obviously in a real app we’d use ES6
export statements etc):