Programming Paradigms - React Prep Review

Introduction

Why Paradigms Matter for React Development

Programming paradigms are essential because they provide different approaches to solving problems and structuring code. Understanding various paradigms allows developers to choose the most effective method for a given task, leading to more efficient, maintainable, and scalable code.

For React Development: Understanding paradigms is crucial because React embraces functional programming principles. While you can write JavaScript in many styles, React works best when you think functionally - using pure functions, immutability, and declarative patterns.

What You'll Master Today

By the end of this lesson, you'll understand why React chose functional programming as its foundation and be able to write code that follows React's core principles. You'll see how different paradigms solve problems and recognize which patterns will serve you best in modern web development.

What are the Main Paradigms in JavaScript?

JavaScript is a versatile language that supports multiple programming paradigms, allowing developers to choose the style that best fits their needs. The main paradigms in JavaScript are:

Core Concept Overview

Imperative Programming

Imperative programming is inherently where everyone starts.

Imperative programming focuses on describing how a program operates. It involves writing sequences of commands that change a program's state. In JavaScript, this is often seen in traditional loops and conditionals.

It is the most straightforward way to write code, as it closely resembles how computers execute instructions. Essentially, we are describing a series of steps that the computer must follow to achieve a desired outcome.

let total = 0;
for (let i = 1; i <= 10; i++) {
  total += i;
}
console.info(total); // Outputs: 55

It's the primary way by which we work with the Document Object Model (DOM), and how to manipulate it. Selecting elements, changing styles, and handling events are all done in an imperative style.

The skills and experience gained from imperative programming are foundational and transfer well to any/all programming languages. Some developers may choose to stick with this paradigm for the entirety of their careers.

However, especially when dealing with front-end development, imperative programming can lead to complex and hard-to-maintain codebases as applications grow in size and complexity. It's the reason why libraries like React have become so popular. They offer a different approach to building user interfaces that can be more manageable. They shift away from imperative programming to a more declarative style, which is a feature of functional programming...

Functional Programming

Functional programming is a paradigm that treats computation as the evaluation of mathematical functions and avoids changing state and mutable data. It emphasizes the use of pure functions, higher-order functions, and immutability.

As this review is transitioning to React, we'll review and explore these features later in the lesson. For now, let's make mention of JavaScript's object oriented programming (OOP) roots.

Object-Oriented Programming (OOP)

For Java/C# Learners: Understanding JavaScript's OOP Approach

(Skip this section if you're not familiar with traditional OOP languages)

JavaScript is/was inspired by some of the principles of OOP, but it is not a traditional OOP language like Java or C++. Instead, it uses prototypes rather than classes for inheritance. However, with the introduction of ES6, JavaScript now supports class syntax, making it easier to implement OOP concepts.

"Everything is an Object" (Almost)

In JavaScript, almost everything is an object except for primitives (numbers, strings, booleans, null, undefined). But here's the cool part: even primitives can temporarily behave like objects when you need to use methods on them.

// Primitives can access methods (they temporarily "borrow" them)
const message = "hello";
console.info(message.toUpperCase()); // "HELLO" - works!

const numbers = [1, 2, 3]; // Arrays are objects
const user = { name: "Alice" }; // Objects are objects
const today = new Date(); // Dates are objects

This is why when you look at MDN documentation, you'll see .prototype mentioned everywhere. Don't worry - you don't need to understand or work with prototypes directly. Just know that it's how JavaScript shares methods between objects behind the scenes. You'll never need to write __proto__ or mess with prototypes directly in modern development.

The Great class Controversy

When ES6 introduced the class keyword in 2015, it was controversial because JavaScript had always been prototypal, not class-based. Many developers called it a "sell out" to traditional OOP languages:

// Modern class syntax (what we used in React until 2018)
class User {
  constructor(name) {
    this.name = name;
  }

  greet() {
    return `Hello, I'm ${this.name}`;
  }
}

const alice = new User("Alice");
console.info(alice.greet()); // "Hello, I'm Alice"

The class syntax won for practical reasons - it made JavaScript more approachable to developers from other languages. However, under the hood, it's still prototypal inheritance. The class keyword is just syntactic sugar. Here, syntactic sugar means it looks like traditional classes, but it doesn't change how JavaScript fundamentally works.

React actually used class components heavily from 2013-2018, but modern React has moved to functional components (which we'll use in this course). You might still see class components in legacy code or certain advanced patterns.

Hands-On Application

Back to Functional Programming - The React Way

Now that we've covered the basics of imperative and OOP paradigms, let's focus on what really matters for React development: functional programming. React is built around functional concepts, and understanding them will make you a much more effective React developer.

Functions as First-Class Citizens

In JavaScript, functions are "first-class citizens," meaning they can be:

  • Stored in variables
  • Passed as arguments to other functions (callbacks)
  • Returned from functions (creating closures)
  • Created at runtime
// Functions stored in variables
const greet = (name) => `Hello, ${name}!`;

// Functions passed as callbacks
const users = ["Alice", "Bob", "Charlie"];
const greetings = users.map(greet); // greet function passed as callback

// Functions returning other functions (closures)
const createMultiplier = (factor) => {
  return (number) => number * factor;
};

const double = createMultiplier(2);
console.info(double(5)); // 10

Pure Functions and Immutability

// Pure function - same input, same output, no side effects
const addTax = (price, taxRate) => price + price * taxRate;

// Side effects = anything that affects the outside world
// (DOM changes, console.log, API calls, etc.)
// Side effects aren't bad - we need them! But pure functions are predictable.

// Working with arrays immutably (React loves this!)
const originalTodos = ["Learn JavaScript", "Build a website"];
const newTodos = [...originalTodos, "Learn React"]; // New array, original unchanged

// Working with objects immutably
const user = { name: "Alice", age: 25 };
const updatedUser = { ...user, age: 26 }; // New object, original unchanged

React-Style Function Patterns

Here are patterns you'll see constantly in React development:

// Preview: React component pattern (pure function that returns UI description)
const WelcomeMessage = ({ userName }) => {
  return `<h1>Hello, ${userName}!</h1>`;
};
// Notice: Pure function, takes input (props), returns output (JSX)

// Data transformation patterns (very common in React)
const users = [
  { id: 1, name: "Alice", isActive: true },
  { id: 2, name: "Bob", isActive: false },
  { id: 3, name: "Charlie", isActive: true },
];

// Filter active users (pure function)
const getActiveUsers = (users) => users.filter((user) => user.isActive);

// Transform user data (pure function)
const getUserNames = (users) => users.map((user) => user.name);

// Combine operations (function composition)
const getActiveUserNames = (users) => {
  return getUserNames(getActiveUsers(users));
};

console.info(getActiveUserNames(users)); // ["Alice", "Charlie"]

Reflection Exercise: Code Review Discussion

Look at this imperative code that manipulates a shopping cart:

let cart = [];
let total = 0;

function addItem(item) {
  cart.push(item);
  total += item.price;
  updateDisplay();
}

function removeItem(itemId) {
  for (let i = 0; i < cart.length; i++) {
    if (cart[i].id === itemId) {
      total -= cart[i].price;
      cart.splice(i, 1);
      break;
    }
  }
  updateDisplay();
}

Reflection Questions:

  1. What paradigm is this code using?
  2. What problems might arise with this approach?
  3. How would you refactor this to be more functional?
  4. What would make this code more predictable and easier to test?

Connecting to React: Declarative vs Imperative

Imperative (what we just saw): "Here's HOW to do it, step by step" Declarative (React's way): "Here's WHAT I want, you figure out how"

// Imperative: Tell the DOM exactly how to update
const button = document.getElementById("myButton");
button.addEventListener("click", () => {
  const counter = document.getElementById("counter");
  const currentValue = parseInt(counter.textContent);
  counter.textContent = currentValue + 1;
});

// Declarative (React-style thinking): Describe what you want
// "When count changes, show the new count"
// "When button is clicked, increment count"
// React handles the HOW for you

Is declarative the same as functional? Not exactly, but they work together beautifully:

  • Functional programming gives us the tools (pure functions, immutability)
  • Declarative style lets us describe what we want rather than how to do it
  • React combines both: functional components that declaratively describe UI

This is why React feels so different from traditional DOM manipulation - you're thinking in terms of "what should the UI look like" instead of "how do I change the UI."

Advanced Concepts & Comparisons

When to Use Each Paradigm

Imperative Programming: "We ain't got time for that!" (But when we do...)

Best for:

  • Direct DOM manipulation (when you need precise control)
  • Performance-critical operations
  • Working with browser APIs
  • Sequential operations that must happen in order

Example: Complex animations, Canvas drawing, Web Audio API

Object-Oriented Programming: "Nice to know, but..."

Best for:

  • Legacy codebases that you inherit
  • Working with existing OOP libraries (rare these days)
  • Maintaining old systems where an organization is too monolithic to refactor it

Real talk: Hard to justify starting a new JavaScript project with classes in 2024. If you encounter OOP in the wild, it's probably legacy code from a company that hasn't adapted to modern practices.

Exception: Angular still uses classes heavily (components, services, dependency injection), so if you're working in the Angular ecosystem, you'll see plenty of OOP. But that's more about framework choice than JavaScript itself.

For React? Classes were used heavily 2013-2018, but modern React is purely functional. You might see class components in old tutorials or legacy projects.

Functional Programming: "This is where it's at!"

Best for:

  • Predictable, testable code
  • User interface development (React!)
  • Data transformation and processing
  • Avoiding bugs related to shared state

Why React chose functional:

  • Predictability: Pure functions make debugging easier
  • Testability: Easy to test functions in isolation
  • Composability: Small functions combine to build complex UIs
  • Performance: Immutability enables efficient change detection

The React Revolution

React didn't just choose functional programming randomly. Here's why it's perfect for UIs:

// Traditional imperative UI updates (vanilla JS DOM manipulation)
function updateUserProfile(user) {
  document.getElementById("userName").textContent = user.name;
  document.getElementById("userEmail").textContent = user.email;
  if (user.isAdmin) {
    document.getElementById("adminBadge").style.display = "block";
  } else {
    document.getElementById("adminBadge").style.display = "none";
  }
  // ... hundreds more lines of manual DOM manipulation
}

// React's declarative approach (preview - this is JSX, not vanilla JS)
function UserProfile({ user }) {
  return (
    <div>
      <h1>{user.name}</h1>
      <p>{user.email}</p>
      {user.isAdmin && <span className="admin-badge">Admin</span>}
    </div>
  );
}
// React figures out how to update the DOM efficiently!

Key Takeaway: Paradigm Flexibility

JavaScript's beauty is that it supports multiple paradigms. You can:

  • Write imperative code when you need precise control
  • Use OOP concepts when modeling complex systems
  • Embrace functional programming for predictable, maintainable code

For this course and React development: We focus on functional programming because it makes UI development more manageable, predictable, and enjoyable. You'll occasionally see the other paradigms, but functional is your primary tool.

Troubleshooting & Best Practices

Common Challenges When Learning Paradigms

"My Functions Have Side Effects Everywhere!"

// ❌ Problematic: Function with hidden side effects
const users = [];
const addUser = (name) => {
  users.push(name); // Side effect!
  console.info("User added"); // Another side effect!
  return users.length;
};

// ✅ Better: Pure function with explicit returns
const addUser = (users, name) => {
  const newUsers = [...users, name];
  return {
    users: newUsers,
    message: "User added",
    count: newUsers.length,
  };
};

"I'm Mutating Data Everywhere!"

// ❌ Problematic: Mutating original data
const updateUserAge = (user, newAge) => {
  user.age = newAge; // Mutates original!
  return user;
};

// ✅ Better: Immutable update
const updateUserAge = (user, newAge) => {
  return { ...user, age: newAge }; // New object
};

"Functional Programming Feels Weird!"

This is normal! If you're coming from imperative programming, functional concepts take time to feel natural. Practice with:

  • Array methods (map, filter, reduce)
  • Spread operator for immutability
  • Pure functions for predictability

Industry Best Practices

For React Development

  1. Default to functional components - Use class components only for legacy purposes
  2. Keep functions pure when possible - Easier to test and debug
  3. Use immutable updates - React's ability to update the DOM efficiently relies on immutability
  4. Compose small functions - Build complex behavior from simple pieces

General JavaScript Practices

  1. Use const by default - Only use let when reassignment is needed
  2. Prefer arrow functions - Cleaner syntax and predictable this behavior
  3. Use array methods over loops - More expressive and functional
  4. Avoid nested mutations - Keep your data transformations shallow and clear

Red Flags to Watch For

⚠️ Warning Signs Your Code Needs Refactoring:

  • Functions that do more than one thing
  • Variables that get reassigned multiple times
  • Code that's hard to test without setting up complex scenarios
  • Functions that behave differently based on when they're called
  • Deep nesting of conditionals and loops

✅ Signs You're on the Right Track:

  • Functions that are easy to name and understand
  • Code that's predictable and testable
  • Data flows that are easy to follow
  • Components that are easy to reason about

Wrap-Up & Assessment

Key Takeaways

🎯 The Big Picture: JavaScript supports multiple programming paradigms, but for React development, functional programming is your best friend.

🔧 Imperative Programming: "How to do it" - Direct, step-by-step instructions. Great for DOM manipulation and precise control, but can get complex quickly.

🏗️ Object-Oriented Programming: "Modeling real-world entities" - Uses classes and objects to structure code. JavaScript's implementation is prototypal, and the class keyword was controversial but practical.

Functional Programming: "What you want, not how to get it" - Pure functions, immutability, and composability. This is React's foundation and your primary tool.

🚀 React Connection: React combines functional programming with declarative UI design. Instead of telling the DOM how to change, you describe what the UI should look like.

Technical Interview Challenge

Record a "selfie-style" video (3-4 minutes) where you:

  1. Explain the paradigms: Describe imperative, OOP, and functional programming in your own words
  2. Refactor this code: Take the shopping cart example from the lesson and refactor it to use functional principles

Your refactor should demonstrate:

  • Pure functions (no side effects)
  • Immutable data updates
  • Clear separation of concerns