Lesson 1.1

Declarative Thinking, Pure Functions, and Why React Feels "Different"

If you're learning React, there's a phrase you'll hear almost immediately: React is declarative. Cool. But what does that actually mean?

What you'll learn
  • The difference between imperative and declarative programming
  • Why declarative code works: predictability
  • The rules of predictable functions
  • How declarative code + pure functions = React
  • Practical examples of imperative vs declarative code

If you try to look up explanations, you get a definition like:

Imperative programming is how you do something. Declarative programming is what you do.

That sounds profound… once you already understand the difference.

For everyone else, it feels like being told "the chicken came first" when you don't even like eggs in the first place.

Let's fix that.

This article brings together two key ideas:

  • Understanding imperative vs declarative programming
  • Understanding predictability, side effects, and pure functions

Together, they form the mental foundation that makes React "click."

Imperative vs Declarative: The Real Explanation

Let's start with metaphors — because metaphors work.

Metaphor #1: Red Lobster

You walk into Red Lobster, approach the host stand, and say:

Imperative (HOW)

"I see an open table under the Gone Fishin' sign. We're going to walk over and sit there."

Declarative (WHAT)

"Table for two, please."

Imperative = you describe the steps.

Declarative = you describe the goal, trusting someone else's system to do the steps.

Metaphor #2: Asking for Directions

You call a friend and say: "How do I get to your house from Walmart?"

Imperative

They give you a long list of turns and exits.

Declarative

"My address is 298 West Immutable Alley."

Declarative solutions assume there's an abstraction that already knows the "how."

GPS. The restaurant employee. The automatic transmission in your car.

Declarative systems always hide an imperative engine underneath.

Real-World Declarative Code

Some languages are inherently declarative:

  • HTML — you describe structure, not how to draw pixels
html
<header>
  <h1>Declarative Programming</h1>
  <p>Sprinkle the word 'declarative' to sound smart</p>
</header>
  • SQL — you describe what data you want, not how to find it
sql
SELECT * FROM Users WHERE Country="Mexico"

You read them and instantly know what's happening.

Imperative JavaScript vs Declarative JavaScript

Let's look at three classic interview-style tasks.

1. Double every number in an array

Imperative

javascript
function double(arr) {
  let results = [];
  for (let i = 0; i < arr.length; i++) {
    results.push(arr[i] * 2);
  }
  return results;
}

Declarative

javascript
function double(arr) {
  return arr.map(item => item * 2);
}

2. Sum every number in an array

Imperative

javascript
function add(arr) {
  let result = 0;
  for (let i = 0; i < arr.length; i++) {
    result += arr[i];
  }
  return result;
}

Declarative

javascript
function add(arr) {
  return arr.reduce((a, b) => a + b, 0);
}

3. Toggle highlight on a button

Imperative (jQuery/DOM)

javascript
$("#btn").click(function () {
  $(this).toggleClass("highlight");
  $(this).text() === "Add Highlight"
    ? $(this).text("Remove Highlight")
    : $(this).text("Add Highlight");
});

Declarative (React)

jsx
<Btn onToggleHighlight={handleToggle} highlight={highlight}>
  {buttonText}
</Btn>

In the declarative version:

  • You describe what the UI should look like
  • React handles how to update the DOM
  • State drives the result

React is declarative because your UI is a function of your state, not a series of manual DOM instructions.

Why Declarative Code Works: Predictability

Let's shift to the second idea — predictability.

As developers, our job is simple:

Make programs predictable.

Every bug ever created came from a mismatch between:

  • what the developer expected
  • what the program did

Predictable code = fewer surprises = fewer bugs.

So what makes code unpredictable?

Two things:

  • Side effects
  • Inconsistent outputs

1. Side Effects

A function should take input → produce output.

Anything else is a side effect.

Examples of functions with side effects:

Mutating external state

javascript
function addTodo(todo) {
  todos.push(todo); // ❌ mutation
}

Relying on external state

javascript
function calculateFinalPrice(price, qty) {
  return price * qty * (1 + TAX_RATE); // ❌ depends on TAX_RATE
}

Changing global environment

javascript
function updateDocumentTitle(title) {
  document.title = title; // ❌ DOM mutation
}

Side effects aren't evil — they're just unpredictable.

But when we isolate them, the rest of the program becomes easier to reason about.

2. Inconsistent Outputs

If a function, given the same input, returns different outputs — it's unpredictable.

Examples:

Math.random()
Date.now()
array.splice()

Even this:

getGithubProfile("user")

Every call returns a different promise.

The Two Rules of Predictable Functions

Rule #1: No Side Effects

A function should not rely on or modify anything outside its scope.

Rule #2: Consistent Outputs

The same input should always return the same output.

These two rules describe what functional programming calls pure functions.

Pure functions are:

  • Composable
  • Reusable
  • Cacheable
  • Testable
  • Easy to reason about
  • Easy to read

They're also the foundation of how React's render cycle works.

Pragmatic Predictability

In the real world, you can't avoid side effects entirely — network requests, DOM updates, localStorage, etc.

But you can contain them.

Example: a pragmatic but still predictable isPrime function with caching:

javascript
let primeCache = { 1: false };

const isPrime = (n) => {
  if (primeCache[n] !== undefined) return primeCache[n];

  for (let i = 2; i <= Math.sqrt(n); i++) {
    if (n % i === 0) {
      return (primeCache[n] = false);
    }
  }

  return (primeCache[n] = true);
};

The impurity is isolated.

Nothing else depends on primeCache.

This is how good abstraction works.

The Big Connection: Declarative Code + Pure Functions = React

React's mental model is built on:

  • Describing what the UI should look like
  • Using pure functions (components)
  • Relying on immutable state
  • Avoiding side effects inside render
  • Letting React handle the how

A React component is essentially:

javascript
UI = f(state)

Not:

text
do this, then change this, then update this…

Declarative thinking + predictable functions =

  • why React feels easier as apps grow

Summary

Declarative programming isn't magic.

Pure functions aren't academic theory.

They're both tools for one thing:

Maximizing predictability.

When your code is predictable:

  • It's easier to test
  • It's easier to reuse
  • It's easier to change
  • It contains fewer bugs
  • And your UI layer (React) becomes refreshingly intuitive

This is the mindset shift that makes React "click."