JSFiddle - React, Tailwind, and code Playground

HTML

<form>
    <label>Name
        <input name='firstName' />
    </label>
    <label>Last Name
        <input name='lastName' />
    </label>
    <label>Age
        <input name='age' />
    </label>
</form>
<div id='result'></div>

CSS

form {
    width:500px;
}
form label {
    display:block;
    /* lol */
    text-align:right;
}

JavaScript

var form = document.forms[0];
var lf = liveForm(form);
var result = document.getElementById('result');
lf.onChange(function () {
    result.textContent = JSON.stringify(lf);
});
setTimeout(function(){
    console.log("HERE! fn:",lf.firstName);
    lf.firstName = "John";
},1000);


function Person() {}

function liveForm(form) {
    var obj = Object.create(Person, {__cbs: { value: [] } });
    Object.defineProperty(obj, "onChange", { value: function (cb) { obj.__cbs.push(cb); } });

    form.addEventListener("keyup", update);
                          
    function update() {
        var updated = serializeForm(form);
        for (var o in updated) {
            Object.defineProperty(obj,o,{
                enumerable:true,
                configurable:true,
                get: function(o){ return updated[o]; }.bind(null,o),
                set: function(o,val){ console.log("hi",updated[o],val);
                    updated[o] = val;
                    form[o].value = val;
                    update();
                }.bind(null,o)
            });
        }
        obj.__cbs.forEach(function (cb) { cb(obj); });
    }
    setTimeout(update,0);
    return obj;
}

function serializeForm(form) {
    var obj = {};
    var items = form.querySelectorAll("input[name]");
    for (var i = 0; i < items.length; i++) obj[items[i].name] = items[i].value;
    return obj;
}