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. Give the scenario above we could create a:
and within that page we could have three additional components:
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 up components is that one component should have one responsibility. This definition is vague, because we could tighten or broaden our scope of that 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 components 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 a 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 from services and so on. The dumb components (generally) just receive inputs from their smart parent component, and their 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:
A lot of the time we will have a single smart component, and that smart component is generally the component that is being routed to (i.e. the “page” component). Then, everything inside of that component is a dumb component. The smart component essentially handles “orchestrating” everything - it gets everything the dumb components need, it gives it to them, and handles any outputs from the dumb components making sure the data goes where it needs to go (e.g. maybe a service needs to be called as a result). This is usually the smart component’s one responsibility - to handle the composition/orchestration of its child components.