When reading the React documentation or blog posts about React, you will have undoubtedly come across the term declarative. One of the reasons why React is so powerful is that it enforces a declarative programming paradigm.
Therefore, to master React, it is essential to understand what declarative programming means and what the main differences between imperative and declarative programming are. The easiest way to approach this is to think about imperative programming as a way of describing how things work, and declarative programming as a way of describing what you want to achieve.
Entering a bar for a beer is a real-life example in the imperative world, where normally you will give the following instructions to the bartender:
- Find a glass and collect it from the shelf.
- Place the glass under the tap.
- Pull down the handle until the glass is full.
- Hand me the glass.
In the declarative world, you would just say "Can I have a beer, please?"
The declarative approach assumes that the bartender already knows how to serve a beer, an important aspect of the way declarative programming works.
Let's move into a JavaScript example. Here we will write a simple function that, given an array of lowercase strings, returns an array with the same strings in uppercase:
toUpperCase(['foo', 'bar']) // ['FOO', 'BAR']
An imperative function to solve the problem would be implemented as follows:
const toUpperCase = input => {
const output = []
for (let i = 0; i < input.length; i++) {
output.push(input[i].toUpperCase())
}
return output
}
First of all, an empty array to contain the result is created. Then, the function loops through all the elements of the input array and pushes the uppercase values into the empty array. Finally, the output array is returned.
A declarative solution would be as follows:
const toUpperCase = input => input.map(value => value.toUpperCase())
The items of the input array are passed to a map function that returns a new array containing the uppercase values. There are some significant differences to note: the former example is less elegant and it requires more effort to be understood. The latter is terser and easier to read, which makes a huge difference in big code bases, where maintainability is crucial.
Another aspect worth mentioning is that in the declarative example, there is no need to use variables, nor to keep their values updated during the execution. Declarative programming tends to avoid creating and mutating a state.
As a final example, let's see what it means for React to be declarative. The problem we will try to solve is a common task in web development: creating a toggle button.
Imagine a simple UI component such as a toggle button. When you click it, it turns green (on) if it was previously gray (off), and switches to gray (off) if it was previously green (on).
The imperative way of doing this would be as follows:
const toggleButton = document.querySelector('#toggle')
toogleButton.addEventListener('click', () => {
if (toggleButton.classList.contains('on')) {
toggleButton.classList.remove('on')
toggleButton.classList.add('off')
} else {
toggleButton.classList.remove('off')
toggleButton.classList.add('on')
}
})
It is imperative because of all the instructions needed to change the classes. In contrast, the declarative approach using React would be as follows:
// To turn on the Toggle
<Toggle on />
// To turn off the toggle
<Toggle />
In declarative programming, developers only describe what they want to achieve, and there's no need to list all the steps to make it work. The fact that React offers a declarative approach makes it easy to use, and consequently, the resulting code is simple, which often leads to fewer bugs and more maintainability.
In the next section, you will learn how React elements work and you will get more context on how props are being passed on a React component.