topic: Vue by Milos Protic relates to: JavaScript, Web Development, Stateless, Functional, Component on September, 15 2019
Understanding Stateless Components in Vue
Introduction - What Is the Application State and Why Do We Need It?
The state management...it's trivial and often not needed in smaller applications, but when it comes to a larger scale, enterprise applications, it becomes a necessity. In a technical, more simpler term, a state is an object containing the latest values used by the application. But, if we look at it from the structural, more abstract angle, it becomes clear that the state is an important piece of the puzzle that enables us to build clean architecture with a strong separation of concerns.
Often, inexperienced developers are unable to predict the need for state management, and how to implement it, therefore, learn the importance of it the hard way. If the state-based components pile up, data managing and sharing between them becomes a nightmare. The more state-based components you have, the more problems will emerge in the long run.
If you are not using an external package for state management, it's a good idea to have as few state-based components as possible with presentational components using that state built around them.
Vue and Stateless (Functional) Components
Stateless components in Vue are nothing else than functional components. But what are functional components? To answer that, we first must understand what functional programming is.
Unlike the Object-Oriented approach which guides the developer to decompose the program into objects, the functional approach encourages the decomposition into small functions which are later used to form an higher-level program. The functions we create do not depend or can alter any external state, which leads to another observation that, for a given input, they will always return the same output.
So, a functional component is a component without state and the option to change it. The output it provides is always (and only) based on the given input. In terms of Vue, this kind of component will give a different output based on the given props.
The Syntax
Vue has a simple way of defining a functional component. All we need to do is to set up the key named functional
. We can do this either in the template (if we are building a single-file component) or in the script section, like this:
<template functional>
<div>Hi from functional/stateless component</div>
</template>
or
export default {
functional: true,
props: {
// ...
},
render(createElement, context) {
return createElement(
'div', 'Hi from functional/stateless component'
);
}
};
In the latest version of Vue, we can omit the
props
in the script section, and all attributes found on the component will be transformed into props
At this point, it's important to mention that the only data being passed to a functional component are the props. These components are fully stateless (no reactive data), they ignore any state passed to them and do not trigger any of the lifecycle methods (created
, mounted
...etc).
Also, we cannot access the instance by using this
keyword, because these components are also instanceless. Instead, everything that the component needs is provided via context
. In render function, it's passed as an additional 2nd argument, after the createElement
.
The official documentation says that the context
is an object containing:
- props: An object of the provided props
- children: An array of the VNode children
- slots: A function returning a slots object
- scopedSlots: (2.6.0+) An object that exposes passed-in scoped slots. Also exposes normal slots as functions.
- data: The entire data object, passed to the component as the 2nd argument of createElement
- parent: A reference to the parent component
- listeners: (2.3.0+) An object containing parent-registered event listeners. This is an alias to data.on
- injections: (2.3.0+) if using the inject option, this will contain resolved injections
Why Do We Need Stateless Components?
So far, we've learned that functional components are stateless and in their core, they are just executable functions accepting some input and giving the output based on it.
What does this mean in terms of their usage? It means that they are fast and cheap to execute, meaning that they are extremely performant and don't require a lot of time to render. Also, think about higher-order components...Ideally, they should not require any state and all they should do is wrap the given child component with additional logic or style. Next, a simple data list display would be a good example of a functional component...
Functional components are ideal for this kind of tasks.
I encourage you to think about more usage examples and write them in the comments section below.
Example
In this quick example, we will create a panel component which will act as a wrapper and will provide the needed styling. The child components will be rendered within the panel body. Let's dive into it.
The panel component looks like this:
export default {
name: 'panel',
functional: true,
props: {
title: String
},
render(createElement, context) {
const slots = context.slots();
const header = createElement('header', {
attrs: { class: 'panel-header'}
}, context.props.title);
const body = createElement('main', {
attrs: { class: 'panel-body'}
}, slots.default);
return createElement('section', {
attrs: { class: 'panel' }
}, [header, body]);
}
}
As mentioned, this component sole purpose is to provide the panel-like (card) styling. It will have header
and main
elements which will hold the panel title and HTML content respectively. The whole magic is done within the render
function by using the createElement
argument. createElement
is a part of the Virtual Dom system implemented in Vue core. Considering that Virtual DOM is not the topic here and if you wish to get into the details, below are links to the official documentation.
The panel CSS looks like this:
.panel {
margin-bottom: .5rem
}
.panel, .panel-header {
border: 1px solid #d3d3d3;
border-radius: 4px;
}
.panel-header, .panel-body, .panel {
padding: .5rem;
}
.panel-header {
background-color:#efefef;
color: #eeeee
}
It's a simple, straight-forward CSS providing some padding and coloring.
Child Components
Now, it's time to make our example alive and show you how useful this kind of component is. To do that, I will create two additional components, one showing a list of cars and another just a simple lorem ipsum text with the requirement that both of them should have the same panel style and look.
Without our panel wrapper component, to achieve the requirement listed above, we would probably have some redundancy between them.
List component:
export default {
name: 'cars',
props: {
data: Array
}
}
template:
<template>
<ul>
<li v-for="car in data" :key="car">{{car}}</li>
</ul>
</template>
A simple text component:
export default {
name: 'lorem-ipsum'
}
template:
<template>
<p>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.
Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.
Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
</p>
</template>
At this point, with child components available, all we need to do is to wrap them in our application with the panel component like this:
<div class="vue-app">
<panel :title="'Car Manufacturers'">
<cars :data="['Mazda', 'Ford', 'Mercedes']"></cars>
</panel>
<panel :title="'Lorem Ipsum'">
<lorem-ipsum></lorem-ipsum>
</panel>
</div>
Do note that these components are used due to the simplicity of the example. In a real application, it could be any kind of component...a table, a form...anything where you see the panel styling suitable.
Here is a working fiddle. Feel free to play around with it.
Conclusion
A great debate is going on among developers, almost as intense as Vue vs React vs Angular debate. This debate is known as Functional Programming vs OOP. Which side are you on? I encourage you to share your thoughts in the comments section below.
I do hope this article will bring closer the use-cases for functional components, and ease your struggle if you had any, whether and where should you use one. If you liked the article, keep me juiced up for more by buying me a coffee and subscribe here or follow me on twitter to stay tuned.
As always, thank you for reading!
Subscribe to get the latest posts delivered right to your inbox