Why React Hooks Embrace Functional Programming: From Pure Functions to useEffect

This article explains how React’s functional nature, rooted in pure functions and side‑effect management, leads to the design of Hooks such as useState and useEffect, and walks through their implementation details and practical considerations.

ELab Team
ELab Team
ELab Team
Why React Hooks Embrace Functional Programming: From Pure Functions to useEffect

From Functional Programming to Hooks

React is a highly functional framework; understanding its functional programming concepts helps grasp core APIs such as setState, render, function components, and Redux.

Functional programming originates from category theory, treating objects as sets and morphisms as functions that map inputs to outputs without side effects.

Pure functions have explicit input‑output via parameters and return values, produce the same output for identical inputs, and have no side effects.

// splice is impure
let arr = [1,2,3,4,5];
arr.splice(0,3); // modifies original array
// slice is pure
arr = [1,2,3,4,5];
arr.slice(0,3); // returns new array

Side Effects

In computer science, a function’s side effect is any impact beyond returning a value, such as modifying globals, parameters, I/O, or DOM queries.

Sending an HTTP request

new Date() / Math.random()

console.log / I/O

DOM queries

Pure functions alone cannot perform useful work; side effects must be managed, often via functors in functional languages.

Historical Background – Hook Origin

React Components

React components are either function components (stateless, no lifecycle) or class components (stateful, lifecycle).

class ComponentA extends React.Component {
  constructor(props) {
    super(props);
    this.state = { displayContent: 'Hello World' };
  }
  render() {
    return <div>{this.state.displayContent}</div>;
  }
}
function ComponentFunctionA = (props) => <div>{props.displayContent}</div>;

Problems

Function components want state and lifecycle

When requirements change, developers may try to give function components state, leading to design issues.

Convert to class component

Lift state to a parent container

Class component lifecycle separates logic

class DemoA extends React.PureComponent {
  constructor(props) {
    super(props);
    this.listener = () => { /* do something */ };
  }
  componentDidMount() {
    document.addEventListener('click', this.listener);
  }
  componentWillUnmount() {
    document.removeEventListener('click', this.listener);
  }
}

Solution – Hook Design

Hooks such as useState and useEffect let function components manage state and side effects while staying mostly pure.

useState

Data‑storage options include class fields, global state, DOM, localStorage, and closures; closures are preferred for minimizing side effects.

function Demo() {
  const [count, setCount] = useState(0);
  return <div onClick={() => { setCount(count++); }}>{count}<div>;
}
var useState = (initState) => {
  let data = initState;
  const dispatch = (newData) => { data = newData; };
  return [data, dispatch];
};

A realistic implementation must distinguish between the mount phase and update phase, store hook objects in the FiberNode, and use a queue to batch state updates.

type Hook = { memorizedState: any, queue: UpdateQueue<any, any> | null, next: any };
type Queue = { last: Update, dispatch: any, lastRenderedState: any };
type Update = { action: any, next: Update };
function mountState(initState) {
  const hook = mountWorkInProgressHook();
  hook.memorizedState = initState;
  const queue = (hook.queue = { last: null, dispatch: null, lastRenderedState: null });
  const dispatch = dispatchAction.bind(null, queue);
  queue.dispatch = dispatch;
  return [hook.memorizedState, dispatch];
}
function dispatchAction(queue, action) {
  const update = { action, next: null };
  let last = queue.last;
  if (last === null) {
    update.next = update;
  } else {
    // link into circular list
    update.next = last.next;
    last.next = update;
  }
  queue.last = update;
  scheduleWork();
}

useEffect

useEffect creates an effect object with a create function, optional destroy function, and a dependency array. Effects are stored in a singly‑linked list on the FiberNode and executed after the commit phase.

type Effect = { create: any, destroy: any, deps: Array, next: any };
type EffectQueue = { lastEffect: Effect };
function useEffect(fn, deps) {
  if (mounted) {
    mountEffect(fn, deps);
  } else {
    updateEffect(fn, deps);
  }
}
function mountEffect(fn, deps) {
  const hook = mountWorkInProgressHook();
  hook.memorizedState = pushEffect('Effect', fn, undefined, deps);
}
function updateEffect(fn, deps) {
  const hook = updateWorkInProgressHook();
  const prev = hook.memorizedState;
  if (!areHookInputsEqual(deps, prev.deps)) {
    hook.memorizedState = pushEffect('Effect', fn, prev.destroy, deps);
  } else {
    hook.memorizedState = prev;
  }
}
function pushEffect(tag, create, destroy, deps) {
  const effect = { create, destroy, deps, next: null };
  const queue = fiberNode.updateQueue = fiberNode.updateQueue || newEffectQueue();
  if (queue.lastEffect) {
    const first = queue.lastEffect.next;
    queue.lastEffect.next = effect;
    effect.next = first;
    queue.lastEffect = effect;
  } else {
    queue.lastEffect = effect.next = effect;
  }
  return effect;
}

During the commit phase, the stored create and destroy functions are invoked in order.

Conclusion

By tracing a problem, defining requirements, designing a solution, and iterating on that design, we understand the motivations behind Hook architecture and can apply similar reasoning to solve real‑world development challenges.

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

frontendReacthooksfunctional programminguseEffectuseState
ELab Team
Written by

ELab Team

Sharing fresh technical insights

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.