Advanced90 min read

Functional Programming in JavaScript

Master functional programming concepts: pure functions, immutability, higher-order functions, and functional composition.

Topics Covered:

Pure FunctionsImmutabilityHigher-Order FunctionsCompositionCurryingRecursion

Prerequisites:

  • JavaScript Functions and Scope

Video Tutorial

Overview

Functional programming is a programming paradigm that treats computation as the evaluation of mathematical functions. Many React patterns are inspired by functional programming. This tutorial covers pure functions, immutability, higher-order functions, function composition, currying, and recursion. Understanding functional programming helps you write better React code.

Pure Functions

Pure functions always return the same output for the same input and have no side effects. Pure Function Characteristics: • Same input → same output • No side effects • Doesn't depend on external state • Predictable and testable Benefits: • Easier to test • Easier to reason about • Can be memoized • Parallelizable

Code Example:
// ✅ Pure function
function add(a, b) {
  return a + b;
}

// Same input always gives same output
console.log(add(2, 3)); // 5
console.log(add(2, 3)); // 5

// ❌ Impure function (depends on external state)
let counter = 0;
function increment() {
  counter++;
  return counter;
}

// ❌ Impure function (side effect)
function logMessage(msg) {
  console.log(msg); // Side effect: logging
  return msg;
}

// ✅ Pure function (no side effects)
function formatMessage(msg) {
  return `[LOG] ${msg}`;
}

// Pure functions in React
function Button({ label, onClick }) {
  // Pure component - same props → same output
  return <button onClick={onClick}>{label}</button>;
}

// Impure component (uses external state)
function Clock() {
  const [time, setTime] = useState(new Date());
  // Uses state, but component itself is still "pure" in React's sense
  return <div>{time.toString()}</div>;
}

Pure functions are predictable and testable. They always return the same output for the same input and have no side effects. React components should be pure when possible.

Immutability

Immutability means data cannot be changed after creation. Instead, create new data structures. Immutability Benefits: • Prevents accidental mutations • Easier to track changes • Better for React (re-renders) • Enables time-travel debugging In JavaScript: • Use const for variables • Create new objects/arrays • Use spread operator • Use immutable libraries

Code Example:
// ❌ Mutation
const user = { name: "Alice", age: 30 };
user.age = 31; // Mutates original object

// ✅ Immutability
const user = { name: "Alice", age: 30 };
const updatedUser = { ...user, age: 31 }; // New object

// ❌ Array mutation
const numbers = [1, 2, 3];
numbers.push(4); // Mutates array

// ✅ Immutable array operations
const numbers = [1, 2, 3];
const newNumbers = [...numbers, 4]; // New array

// Immutability in React
function Component() {
  const [items, setItems] = useState([1, 2, 3]);
  
  const addItem = (item) => {
    // ✅ Create new array
    setItems([...items, item]);
    
    // ❌ Don't mutate
    // items.push(item);
    // setItems(items);
  };
  
  const updateItem = (index, newValue) => {
    // ✅ Create new array with updated item
    setItems(items.map((item, i) => 
      i === index ? newValue : item
    ));
  };
}

Immutability prevents bugs and makes code easier to reason about. Always create new objects/arrays in React instead of mutating existing ones. Essential for React's re-rendering.

Function Composition

Function composition combines simple functions to build complex ones. Composition: • Combine functions • Output of one is input of next • Build complex behavior from simple parts • More readable than nested calls Benefits: • Reusable functions • Easier to test • More readable • Follows single responsibility

Code Example:
// Simple functions
const add = (a, b) => a + b;
const multiply = (a, b) => a * b;
const square = x => x * x;

// Composition
const addThenSquare = (a, b) => square(add(a, b));
console.log(addThenSquare(2, 3)); // 25

// Compose utility
const compose = (...fns) => (value) => 
  fns.reduceRight((acc, fn) => fn(acc), value);

const pipe = (...fns) => (value) => 
  fns.reduce((acc, fn) => fn(acc), value);

// Usage
const process = pipe(
  (x) => x + 1,
  (x) => x * 2,
  (x) => x.toString()
);

console.log(process(5)); // "12"

// Composition in React
const withAuth = (Component) => (props) => {
  if (!props.isAuthenticated) {
    return <Login />;
  }
  return <Component {...props} />;
};

const withLogging = (Component) => (props) => {
  console.log("Rendering:", Component.name);
  return <Component {...props} />;
};

const EnhancedComponent = withLogging(withAuth(MyComponent));

Function composition combines simple functions to build complex behavior. Makes code more reusable and readable. Used in React for higher-order components and hooks.

Currying

Currying transforms a function with multiple arguments into a sequence of functions with single arguments. Currying: • Function returns another function • Each function takes one argument • Partial application • More flexible function usage Benefits: • Partial application • Function reuse • More flexible APIs • Functional composition

Code Example:
// Regular function
function add(a, b, c) {
  return a + b + c;
}

// Curried function
function curriedAdd(a) {
  return function(b) {
    return function(c) {
      return a + b + c;
    };
  };
}

// Arrow function currying
const curriedAdd = a => b => c => a + b + c;

console.log(curriedAdd(1)(2)(3)); // 6

// Partial application
const add5 = curriedAdd(5);
const add5And10 = add5(10);
console.log(add5And10(15)); // 30

// Currying utility
function curry(fn) {
  return function curried(...args) {
    if (args.length >= fn.length) {
      return fn.apply(this, args);
    }
    return function(...nextArgs) {
      return curried.apply(this, args.concat(nextArgs));
    };
  };
}

const curriedMultiply = curry((a, b, c) => a * b * c);
console.log(curriedMultiply(2)(3)(4)); // 24
console.log(curriedMultiply(2, 3)(4)); // 24
console.log(curriedMultiply(2)(3, 4)); // 24

// Currying in React
const handleChange = (field) => (event) => {
  setFormData({
    ...formData,
    [field]: event.target.value
  });
};

<input onChange={handleChange("name")} />
<input onChange={handleChange("email")} />

Currying transforms multi-argument functions into sequences of single-argument functions. Enables partial application and more flexible function usage. Useful in React for event handlers.

Conclusion

Functional programming provides powerful patterns for writing maintainable code. You've learned pure functions, immutability, composition, and currying. These concepts are fundamental to React development. Practice these patterns and apply them in your React applications for cleaner, more testable code.