Imperative vs. Declarative UI Development: A Tale of Two Approaches
In the world of web development, building user interfaces (UIs) involves handling user interactions and keeping the UI in sync with the underlying data. Two main approaches dominate this landscape: imperative and declarative. Today, we'll dive into these concepts, explore their key differences, and see how they play out in practical examples using vanilla JavaScript and React.
Imperative Approach: Taking Control, Step-by-Step
The imperative approach involves explicitly instructing the code on how to achieve a specific UI outcome, step-by-step. Let's consider a simple example using vanilla JavaScript where we want to create a clickable heading element that changes color and text content when clicked:
<!DOCTYPE html>
<html>
<body>
<h1 id="message">Click me</h1>
<script>
const messageElement = document.getElementById('message');
let isClicked = false; // Track click state
messageElement.addEventListener('click', function() {
if (isClicked) {
// Revert to original state (black)
messageElement.textContent = 'Click me';
messageElement.style.color = 'black';
isClicked = false;
} else {
// Change to red on first click
messageElement.textContent = 'You clicked!';
messageElement.style.color = 'red';
isClicked = true;
}
});
</script>
</body>
</html>
In this example, we aim to achieve the following UI behavior
We have a heading element (
<h1>
) with the text "Click me" displayed initially.When the user clicks the heading, the text changes to "You clicked!" and the color changes to red.
Clicking the heading again reverts it to its original state ("Click me" with black color).
Declarative Approach: Describe the What, Let the Library Handle the How (React Example)
The declarative approach, in contrast, focuses on describing the desired state of your UI based on certain conditions. A library like ReactDOM handles the underlying DOM manipulation efficiently.
Declarative in Action: React's Take on Clickable Headings
Let's see how React achieves the same button color change functionality declaratively:
import React, { useState } from "react";
function Heading() {
const [isClicked, setIsClicked] = useState(false);
const handleClick = () => {
setIsClicked(!isClicked); // Toggle state on click
};
return (
<h1 onClick={handleClick} style={{ color: isClicked ? "red" : "black" }}>
{isClicked ? "You clicked!" : "Click me"}
</h1>
);
}
export default Heading;
Explanation of React Code:
We import
useState
from React to manage the component state.The
Heading
function defines our component.We initialize the
isClicked
state variable tofalse
usinguseState
.The
handleClick
function updates the state by togglingisClicked
on click.JSX elements define the UI. Here:
The heading element (
<h1>
) is wrapped with anonClick
handler that triggershandleClick
.The heading's style is set conditionally based on
isClicked
: red for clicked, black for unclicked.The heading content also changes based on
isClicked
.
Key Differences and Benefits of Declarative Programming
Readability and Maintainability:
Declarative code focuses on the desired UI state, making it easier to understand the logic and reason about changes.
Imperative code with step-by-step DOM manipulation can become verbose and harder to follow, especially in complex UIs.
Debugging:
Declarative code makes debugging easier because changes in state lead to predictable UI updates. You can trace state changes to identify the root cause of UI bugs.
Debugging imperative code can be challenging, as you need to track down the exact lines that manipulate the DOM and identify unintended side effects.
Efficient Updates (React):
React utilizes a virtual DOM, an in-memory representation of the UI. When the state changes, React efficiently compares it with the real DOM and updates only the necessary parts.
Imperative updates often require manual DOM manipulation, which can be inefficient and error-prone, especially for complex UIs with frequent updates.
Focus on UI State:
Declarative code encourages thinking in terms of the UI state and how it affects the UI's appearance, promoting a more reactive and data-driven approach.
Imperative code can get bogged down in the details of DOM manipulation, leading to code tightly coupled with the specific DOM structure.
Reusability and Composability:
Declarative components tend to be more reusable because they focus on the desired UI state rather than the implementation details.
Imperative components can be tightly coupled with specific DOM manipulation logic, making them less reusable across different UI scenarios.
Choosing Your Approach
While both approaches can achieve UI manipulation, the declarative approach with React offers advantages in readability, maintainability, and efficient UI updates. As your applications grow, these benefits become even more crucial.
In conclusion, understanding the differences between imperative and declarative UI development can significantly impact your development workflow. By leveraging the declarative approach with libraries like React, you can create cleaner, more maintainable, and efficient UIs.
References
Subscribe to my newsletter
Read articles from Sanjit Gawade directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by
Sanjit Gawade
Sanjit Gawade
ME IT'22 GEC GOA