That "Aha!" Moment: Discovering Reactivity

December 2, 2024 (7mo ago)

The best way to predict the future is to invent it.

[— Alan Kay]

When I first started learning web development, the Document Object Model (DOM) was just a static thing to me. You wrote some HTML, and if you wanted to change something on the page, you had to grab it with JavaScript and manually update it. It was a very direct, imperative process: "find this element, now change its text."

I spent countless hours writing code like this:

// The old way: manual DOM manipulation
document.getElementById('my-button').addEventListener('click', () => {
  let count = parseInt(document.getElementById('my-counter').innerText);
  count++;
  document.getElementById('my-counter').innerText = count;
});

This worked, but it was messy. For every little change, I had to write explicit instructions. As my applications grew more complex, keeping track of all these manual updates became a nightmare. I was spending more time managing the DOM than building features.

The Idea of a "Reactive" DOM

I remember thinking, "What if the DOM could just... update itself?" What if I could just change a variable, and the parts of the page that depended on that variable would automatically reflect the new value? At the time, it seemed like a fantasy. The DOM wasn't a living, breathing thing; it was a rigid structure that I had to manipulate.

Then I found React. And suddenly, my fantasy was a reality.

React introduced me to the concept of declarative rendering. Instead of telling the DOM how to change, I could just declare what the UI should look like for a given state. When the state changed, React would take care of updating the DOM for me. It was magic.

Building a Simple Reactive System in Vanilla JS

To truly appreciate what frameworks like React do for us, it can be helpful to build a tiny version of a reactive system ourselves. It turns out, the core idea isn't as complicated as you might think. We can use a JavaScript Proxy to watch for changes to an object and automatically trigger updates.

Let's try to build a simple counter, but this time, we'll make it reactive.

First, the HTML:

<p>Count: <span id="counter">0</span></p>
<button id="increment-btn">Increment</button>

Now, the JavaScript:

// Our reactive state object
const state = {
  count: 0,
};
 
// A function to update the DOM
function updateDOM() {
  document.getElementById('counter').innerText = state.count;
}
 
// A Proxy to watch for changes to our state
const reactiveState = new Proxy(state, {
  set(target, property, value) {
    // Update the property
    target[property] = value;
 
    // Re-render the component
    updateDOM();
 
    return true;
  },
});
 
// Event listener for the button
document.getElementById('increment-btn').addEventListener('click', () => {
  // We just update the state, and the DOM will react automatically
  reactiveState.count++;
});
 
// Initial render
updateDOM();

Look at the event listener now. All we do is reactiveState.count++. We don't have to manually find the element and update its text. The Proxy intercepts the change to our count property and calls updateDOM() for us. This is the heart of reactivity.

How React Takes This to the Next Level

Of course, this is a very simplified example. React does this on a much larger and more optimized scale. It creates a "virtual DOM"—a lightweight copy of the actual DOM—and when the state changes, it compares the new virtual DOM with the old one to figure out the most efficient way to update the real DOM. This process, called "reconciliation," is why React is so fast and powerful.

But the core principle is the same: state drives the UI. As a developer, you can focus on managing your application's state, and you can trust React to handle the messy business of updating the DOM.

Conclusion

That shift from imperative to declarative programming was a huge turning point for me. It was the moment I realized that the DOM didn't have to be a static document that I had to manually poke and prod. It could be a dynamic, reactive surface that responds to changes in my application's state.

Discovering reactivity didn't just make me a better developer; it made me fall in love with web development all over again. It opened my eyes to a whole new way of building user interfaces, and I've never looked back.