DEV Community

Cover image for How Was State Managed in Frontend Development in the Old Days and Its Evolution?
Shrey Saraswat
Shrey Saraswat

Posted on

How Was State Managed in Frontend Development in the Old Days and Its Evolution?

Open a jQuery codebase from 2012 and try to answer one question: where is the cart count stored?

Maybe it's a JavaScript variable. Maybe it's the text inside <span class="cart-badge">. Maybe it's a data-count attribute on a button. Often, it's all three, and they disagree.

That disagreement is the problem React, Redux, and every state library after them were built to solve. Here is how it looked before them.

The simplest case: a counter

Plain JavaScript:

<h2 id="count">0</h2>
<button id="increment">Increment</button>

<script>
  let count = 0;

  document.getElementById("increment").addEventListener("click", () => {
    count++;
    document.getElementById("count").textContent = count;
  });
</script>
Enter fullscreen mode Exit fullscreen mode

jQuery (2006 onward):

<h2 id="count">0</h2>
<button id="increment">Increment</button>

<script>
  let count = 0;

  $("#increment").on("click", function () {
    count++;
    $("#count").text(count);
  });
</script>
Enter fullscreen mode Exit fullscreen mode

React:

import { useState } from "react";

function Counter() {
  const [count, setCount] = useState(0);

  return (
    <>
      <h2>{count}</h2>
      <button onClick={() => setCount(count + 1)}>Increment</button>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

jQuery made selecting elements and wiring events much nicer, but look at what it did not change: you still update the variable and you still tell the DOM what to do. Two steps, both manual.

React removes the second step. You change the state, and React works out the DOM changes.

The difference is imperative (do these DOM operations) versus declarative (here is what the UI looks like for this state).

Where it breaks: the cart

A counter has one number and one place that shows it. Real apps don't.

Think of a shop with a navbar badge, a product page, a cart page, and a checkout summary. Adding an item has to update all of them:

let cart = [];

function addToCart(product) {
  cart.push(product);

  $(".cart-badge").text(cart.length);
  $("#cart-total").text(calculateTotal(cart));
  $("#product-" + product.id + " .add-btn").text("In cart");

  if (cart.length === 1) {
    $("#empty-cart-message").hide();
  }
  // ...and the checkout summary? And the "remove" flow?
}
Enter fullscreen mode Exit fullscreen mode

This is an illustrative example, not code from a specific project, but the shape is very familiar. Every feature that touches the cart needs to remember every place the cart is displayed. Miss one and the UI quietly lies to the user.

Two common failure modes:

  • Forgotten update: You add a "remove" button and forget to refresh the badge.
  • DOM as the database: Someone reads parseInt($(".cart-badge").text()) because it was convenient. Now the display is the source of truth, and formatting changes like "1 item" can break logic.
flowchart TD
    A[User clicks Add to cart] --> B[Update cart variable]
    B --> C[Update navbar badge]
    B --> D[Update product button]
    B --> E[Update cart total]
    B --> F[Update checkout summary]
    F -. forgot this one .-> G[UI out of sync]

How the tools evolved

The eras overlap, and none of them fully disappeared, but roughly:

Era Approach What it helped with What was still manual
Static HTML (1990s) Reload the page from the server Nothing to sync Any interactivity
Vanilla JS (late 1990s to 2000s) Variables + DOM APIs Dynamic pages Selecting and updating everything
jQuery (2006) Selectors, events, simpler DOM Cross-browser pain, less code State-to-DOM sync
Backbone / AngularJS (about 2010) Models, views, bindings Structure, some automatic syncing Complexity, tricky data flow
React (2013) Components render from state DOM updates Sharing state across components
Redux, MobX, Context, Zustand (2015 onward) Centralized or specialized stores Shared state at scale Choosing the right tool

Backbone gave you models that fire change events, but you still wrote the render logic. AngularJS added two-way binding, which was convenient and also made it harder to see which change caused which update.

Then React's pitch was simple: stop writing the update step. Describe the UI as a function of state.

The same cart, declaratively

function Navbar({ cart }) {
  return <span className="cart-badge">{cart.length}</span>;
}

function CartSummary({ cart }) {
  const total = cart.reduce((sum, item) => sum + item.price, 0);
  return <p>Total: {total}</p>;
}

function App() {
  const [cart, setCart] = useState([]);

  const addToCart = (product) => setCart((prev) => [...prev, product]);

  return (
    <>
      <Navbar cart={cart} />
      <ProductList onAdd={addToCart} />
      <CartSummary cart={cart} />
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

There is no "update the badge" code. The badge is cart.length, always.

That solves rendering. It doesn't solve sharing: once many distant components need cart, passing props through layers gets awkward. That is the gap Redux filled in 2015 with a single store and explicit updates:

// Redux Toolkit
const cartSlice = createSlice({
  name: "cart",
  initialState: [],
  reducers: {
    added: (state, action) => {
      state.push(action.payload);
    },
  },
});
Enter fullscreen mode Exit fullscreen mode

Whether you need Redux, Context, or Zustand is a separate question. Plenty of apps are fine with plain React state.

What to take from this

  • Old-style bugs came from duplicated state: a variable, the DOM, and sometimes an attribute, all claiming to be the truth.
  • React's biggest change was not "components." It was one source of truth that the UI is derived from.
  • State management libraries solve sharing and coordinating state, not the basic rendering problem.
  • You can still see this today: any time you read data back out of the DOM in React (ref.current.value everywhere, document.querySelector), you are partly back in the old model.

If an interviewer asks

Before React, state lived in JavaScript variables, globals, browser storage, and often the DOM itself. jQuery simplified DOM updates, and MVC frameworks like Backbone and AngularJS added structure and bindings. As apps grew, keeping data and UI consistent got hard. React introduced a declarative, component-based model where UI is derived from state, and libraries like Redux later made shared state more predictable.

And one distinction worth making: state existed long before React. React changed how state connects to the UI.

Your turn

If you worked in the jQuery or Backbone days, what was the worst "UI out of sync" bug you remember? And if you started with React, have you ever caught yourself reading values back from the DOM instead of state?

Top comments (0)