The container component
On my last lecture, I introduced you to the Archetype Component Pattern. On this story, I’ll be talking about the component that uses all the Archetypes, The container component.
The container component is a javascript class that manages the state of our application and calls the required Archetypes to create a new feature. It should use different services and typically it will be included as a new route in our main component. It also includes an action dispatcher method to handle the user interactions.
A container component looks like:
// HTML
<archetype [config]="config
(actions)="actionsDispatcher($event)"></archetype>// JS
export class AppComponent {
// Object to store the state of the app
private _state = {};
public config: ConfigurationModel = {...DEFAULT_CONFIGURATION};
public actionsDispatcher(action: ActionsModel) {
switch(action.type) {
case types.MAIN_BUTTON_CLICKED: {
// Do something
break;
}
case types.SECONDARY_BUTTON_CLICKED: {
// Do something
break;
}
default: {
throw new Error('Action not captured: ' + action.type);
}
}
} private _setState() {
const newState = {...state};
// Edit the new state
this._state = {...newState}; return newState;
}
}
The default configuration
public config: ConfigurationModel = {...DEFAULT_CONFIGURATION};
The configuration object should use a scheme defined in the Archetype. On that file, you will find the first state of the Archetype render in your container. Be careful when you assign this configuration, this file could be used by others components, it’s a must to create a copy of the configuration object before it’s assignation to prevent undesired behaviors when you need to mutate this information.
The state
private _state = {};
private _setState() {
const newState = {...state};
// Edit the new state
this._state = {...newState}; return newState;
}
The state object is the unique source of truth for our feature. This means that this object can’t be mutated for other components. As you see in the above code we create a private method who create a new instance of the object and update the current state of the application.
The dispatcher
public actionsDispatcher(action: ActionsModel) {
switch(action.type) {
case types.MAIN_BUTTON_CLICKED: {
// Do something
break;
}
case types.SECONDARY_BUTTON_CLICKED: {
// Do something
break;
}
default: {
throw new Error('Action not captured: ' + action.type);
}
}
}
The dispatcher method is responsible for receiving, filter and executing the desired behavior on each action. There are different techniques to achieve this but for me, the cleanest method is to use a switch statement. If you want to check different solutions for this please take a look on: https://hackernoon.com/rethinking-javascript-eliminate-the-switch-statement-for-better-code-5c81c044716d and https://medium.com/tandemly/whats-wrong-with-the-switch-statement-in-javascript-c560e8ea3c0b
Conclusions
Each Container Component should be a new feature of your own application. Things like lazy loading and scoped styling should be applied at this level. This schema allows the creation of frontend applications with low coupling and high cohesion because it allows you to easily differentiate each feature in your source code.
Is important to centralize the state of the application in a single place. It allows developers to know where to edit the information of the application and prevent the uncontrollable cascading effect.
In the next lecture, I’ll be talking about a better way to centralize the state of the application using Angular services.
Full working example
Thanks for reading. Hope you enjoy it.
Copyediting: Camilo Andrés Calderón-Acevedo