Mimicking React’s useState Hook with JavaScript Proxy Object

muhammad moin
Tensor Labs
Published in
3 min readJul 26, 2024

--

Ever wished you could bring the reactivity of React’s useState to your vanilla JavaScript projects? You’re in luck! With JavaScript’s Proxy object! 🎉 Today, we're diving into how this incredible feature can help you mimic React's reactivity. But first, let's embark on a little storytelling journey

A Tale of Manual Updates

Let me share a story from my own experience. I was working on a project where I needed to update values on the screen whenever the underlying data changed. It was a challenge because I had to manually write code to update the display each time the data changed. It felt like a real hassle and made the process more complicated. 😩

Wish I had known about the Proxy object back then! We can use it to automatically trigger UI updates whenever the data changes, much like React’s state. 🌟 Let’s break down how we can achieve this with three simple code snippets.

Part 1: Basic Proxy Setup

First, let’s set up our basic Proxy that logs changes whenever the object’s properties are updated. Here’s how we start:

document.addEventListener('DOMContentLoaded', () => {
const target = { count: 0 };

const handler = {
set: function (obj, prop, value) {
if (prop === 'count') {
console.log(`Setting count to ${value}`);
}
obj[prop] = value;
return true;
}
};

const proxy = new Proxy(target, handler);
});

In this code, we’re defining a Proxy with a handler that listens for changes to the count property. Whenever count is updated, a message is logged to the console. Simple, but super effective for understanding how Proxy works!

Part 2: Connecting Proxy to the UI

Now, let’s take it up a notch and connect our Proxy to update the UI. Here’s how we do it:

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Counter</title>
<link rel="stylesheet" href="styles.css">
</head>
<body>
<div class="counter-container">
<div class="count-display">Count: <span id="count">0</span></div>
<div class="button-container">
<button id="subtract-btn" class="btn">-</button>
<button id="add-btn" class="btn">+</button>
</div>
</div>
<script src="script.js"></script>
</body>
</html>
document.addEventListener('DOMContentLoaded', () => {
const target = { count: 0 };

const handler = {
set: function (obj, prop, value) {
if (prop === 'count') {
console.log(`Setting count to ${value}`);
const countElement = document.getElementById('count');
countElement.innerText = value;
}
obj[prop] = value;
return true;
}
};

const proxy = new Proxy(target, handler);

document.getElementById('add-btn').addEventListener('click', () => {
proxy.count += 1;
});

document.getElementById('subtract-btn').addEventListener('click', () => {
proxy.count -= 1;
});

In this setup, the Proxy updates the UI element when the count property changes.

Part 3: Adding Validations with Toast Notifications

Finally, let’s add a toast notification to handle validations and prevent negative counts. Here’s the updated code snippet that includes a toast for user feedback:

//lets update the handler for the toast function
const handler = {
set: function (obj, prop, value) {
if (prop === 'count') {
if (value < 0) {
showToast('Negative numbers aren\'t allowed');
return false; // Prevent setting the value if negative
}
console.log(`Setting count to ${value}`);
const countElement = document.getElementById('count');
countElement.innerText = value;
countElement.classList.add('change');
setTimeout(() => {
countElement.classList.remove('change');
}, 300);
}
obj[prop] = value;
return true;
}
};

function showToast(message) {
const toast = document.getElementById('toast');
toast.innerText = message;
toast.classList.add('show');
setTimeout(() => {
toast.classList.remove('show');
}, 3000); // Hide toast after 3 seconds
}

This little function displays a toast message whenever an invalid action (like going below zero) occurs.

Conclusion

So, the next time you’re building a web app and want seamless reactivity, consider using the Proxy object. It’s a game-changer that brings both elegance and simplicity to your JavaScript projects. Happy coding! 🚀

--

--