Skip to main content

What is a pure Function?

A pure function always returns the same output for the same input and produces no side effects outside its own scope.

Theory

TL;DR

  • A pure function is like a vending machine: same coins in, same snack out, no mess left behind
  • Two rules: same input always gives the same output, and no side effects (no mutations, no logs, no API calls)
  • Output depends only on arguments, never on global state, DOM, or values like Date.now()
  • React's useMemo and React.memo rely on purity to skip re-renders safely

Quick example

javascript
// Pure: depends only on arguments const add = (a, b) => a + b; add(2, 3); // 5, always // Impure: reads external state const now = () => Date.now(); // different result each call // Impure: mutates external variable let count = 0; const increment = () => count++; // side effect

add only touches its arguments. Date.now() reads from the clock, external state it doesn't control. increment modifies count outside its own body. Those last two are impure.

Key difference

Pure functions depend only on the arguments passed in. They don't read global variables, touch the DOM, or access anything that lives outside their own body. That makes the output predictable: call add(2, 3) a thousand times and you always get 5. Impure functions break that contract the moment they read or write something external.

When to use

  • Math or logic over props/state (calculate a cart total, filter a list): pure function
  • Wrapping in useMemo or React.memo to avoid unnecessary re-renders: pure function required
  • API calls, DOM mutations, timers: impure by nature, isolate them in useEffect
  • Shared utility helpers used across components: pure, because they are easy to test and reuse

How the engine handles this

V8 doesn't check purity explicitly. It infers predictability from the absence of external mutations during JIT compilation, which opens the door to optimizations like inline caching. React does something similar at a higher level: it compares props shallowly and skips re-renders when inputs haven't changed. That mechanism only works correctly if your function is actually pure and returns the same output for the same props.

Common mistakes

Mutating the argument

javascript
// Looks harmless, breaks React state const badSum = (numbers) => { numbers.push(0); // mutates the original array! return numbers.reduce((a, b) => a + b, 0); }; // Fixed const goodSum = (numbers) => { return [...numbers].reduce((a, b) => a + b, 0); };

React expects immutability. Mutating an array that came in as a prop produces stale state bugs that are hard to trace. In code reviews, this pattern shows up more often than you'd expect, usually hidden inside a function that looks completely harmless.

Reading global state

javascript
let counter = 0; const getNextId = () => counter++; // different result every call // Fixed: pass it as an argument const getNextId = (counter) => counter + 1;

Redux forbids this pattern in reducers for exactly this reason.

console.log inside a "pure" function

javascript
const multiply = (a, b) => { console.log('computing'); // side effect return a * b; };

The log doesn't change the return value, but it changes observable program behavior. React can't safely memoize this.

Real-world usage

  • React useMemo: wrap expensive pure computations (filtering large lists) so React skips re-running them on every render
  • Redux reducers: must be pure, take state and action, return new state without mutations (Redux Toolkit uses Immer under the hood)
  • Lodash FP / Ramda: all functions pure by design, safe to compose into pipelines

Follow-up questions

Q: What is referential transparency?
A: A function call can be replaced with its return value without changing program behavior. add(2, 3) and 5 are interchangeable anywhere in the code.

Q: Why does React care whether a function is pure?
A: React.memo and useMemo rely on the assumption that the same input produces the same output. If a function is impure, caching its result leads to wrong UI state.

Q: Does useCallback make a callback pure?
A: No. useCallback memoizes the function reference so it doesn't change between renders. But if the callback reads or mutates external state, it's still impure. Reference stability and purity are separate things.

Q: Can a function that mutates its argument ever be pure?
A: No. Mutation is a side effect by definition. Even if the return value looks correct, the function changed something outside its own body.

Examples

Basic: tax calculator

javascript
// Pure - safe to wrap in useMemo const calculateTax = (amount, rate) => amount * (1 + rate); calculateTax(100, 0.08); // 108 calculateTax(100, 0.08); // 108, every time

Same inputs, same output, no external reads. React can cache this result and skip recalculation until amount or rate actually changes.

Intermediate: filtering todos in a component

javascript
// Pure filter function const filterTodos = (todos, filter) => { return todos.filter(todo => { if (filter === 'active') return !todo.completed; if (filter === 'completed') return todo.completed; return true; }); }; // In component const visibleTodos = useMemo( () => filterTodos(todos, filter), [todos, filter] );

filterTodos only reads its arguments. Wrapping it in useMemo means React re-runs the filter only when todos or filter actually changes, not on every render. This is the pattern from React's own docs for expensive list operations.

Short Answer

Interview ready
Premium

A concise answer to help you respond confidently on this topic during an interview.

Finished reading?