Smart and Dumb (Presentational) Components in Angular
We already have some idea of the what components are all about. We have discussed how our applications are essentially a tree of nested components starting from our root component.
Our root component might use the <router-outlet>
to control which page
components are displayed (which are just standard components that are serving
the role of displaying some “page” like the home page or the settings
page). Those components might then contain additional components that serve
various roles — like displaying a form and accepting user input, or displaying
a search bar, or displaying a list with items that can be clicked.
What is not immediately obvious is how we should go about deciding when to create a component and what it should be. Given the scenario above we could create a:
HomeComponent
page
and within that page we could have three additional components:
SearchBarComponent
FormComponent
ListComponent
Or… we could just define everything directly in the HomeComponent
rather
than creating all these separate components. To help us with this, we are going
to revisit the single-responsibility principle and introduce the concept of
smart and dumb (or presentational) components.
The Single Responsibility Principle
As we discussed in the last lesson, the single-responsibility principle suggests that:
Every class should have only one responsibility
The general idea in the context of components is that one component should have one responsibility. This definition is vague, because we could tighten or broaden our scope of what that one responsibility is. Really, this is just something to get us thinking about the architecture — we don’t always have to strictly adhere to it, but we should always consider if breaking things up into more components might make our application more adaptable/maintainable/clean.
This will help us to determine when we should create a new component. Imagine we have a home page. Within that home page we have a header, a search bar, a card, and a list. We could just add everything we need to the home component.
The home component’s responsibilities might then look like this:
- Render all of the code required for all of these UI elements
- Set up bindings for the search bar
- Listen for changes in input on the search bar
- If there is a new search, fetch the new data
- Render the data inside of the list
- Set up event bindings for the list to detect clicks
- …and so on
We could quite comfortably say that this component has more than one responsibility. Instead, we might break this up into multiple components. Specifically, we could break this up into smart and dumb components.
Smart and Dumb Components
The idea with smart and dumb components is that we break up our components into smart components that know what is happening in the application in a broader sense, and dumb (or presentational) components that don’t know anything about the application.
What this generally means is that smart components will play the role of composing different components together, they can inject dependencies, make calls to services, request streams/signals from services and so on. A dumb component (generally) just receives inputs from its smart parent component, and its only way to communicate with the rest of the application is through its outputs. They (generally) don’t inject dependencies and they don’t make calls to services. This is why they are “dumb”, they don’t know what the goals of the application are on a broader scale, they get their inputs and they give outputs as a result (or maybe they don’t even do that). The dumb components don’t even really know why they are getting certain inputs or why they are giving certain outputs, they just do what they are supposed to do.
To give this a little more context, let’s break up that home page example we just looked at into smart and dumb components:
Smart components:
HomeComponent
Dumb components:
HeaderComponent
ListComponent
SearchBarComponent
CardComponent