A few years ago, I learned enough React to cobble together a project I had in mind (RiffTube). Some parts of it I liked, and others I didn't. I've always leaned more toward vanilla JavaScript, and been wary of frameworks.
Recently I began reworking RiffTube, and updating Node packages caused everything to break. I eventually got things working again, but the process reminded me of what I don't love about React.
The idea for a framework started forming in my mind. There were two core pieces:
- Components, similar to React
- Communication via broadcasting
It seems to me that React's strategy for handling state is both redundant and at adds with the way JavaScript does everything else. The state of a web app can be specified by listing out all the values of all the variables (such as, and including, all the objects and all their properties).
React adds another layer of variables, etc., on top of that, which is what seems redundant. And then, when it's "official" state gets updated, it re-renders the component, which seems to me at odds with the JavaScript ethos.
So I started working on hearsay.
In hearsay, there is no "special" state. So if you want a component to do something, like update a property/UI, you can broadcast a message. You can specify a recipient, so that the appropriate recipient can respond -- but every component can "hear" the message, and decide if they need to respond or not.
Components are implemented with a fairly lightweight custom element (MDN article). The custom element, <hear-say>
, provides a few capabilities and behaviors.
-
src
attribute: Specify the path to an HTML file.- The contents of this file will be attached as the shadow DOM for the custom element.
- A
<script>
element can be used to call a specialsetup()
function allowing you to:- Attach custom element callbacks.
- Specify a special
init()
method. - Specify a special
react()
method to respond to messages that are broadcast.
-
slot()
method:A helper method for filling slots in the shadow DOM.
-
props
attribute:Inspired by React, I wanted to be able to pass data into a component. Using this attribute, you can specify an object literal with regular JavaScript code.
The props
attribute ended up being more complicated to implement than I realized at first. In particular, if props
started out with a value like { n: Math.random() }
, then doing this.props = { ...this.props, foo: 42 }
would overwrite the code expression with a value, like { n: 0.123, foo: 42 }
.
So I wanted to come up with a way to make using props
easy and intuitive. The solution I came up with involves (what I think is) a pretty neat use of Proxy. The next post will be all about that.
Here is an example of a very simple hearsay component. It includes code that calls broadcast()
, and also a react()
method that responds to the message.
Clicker.html:
<script>
setup({
init: self =>
{
self.num = 0;
self.slot("slot", self.num);
},
react: (self, data, recipient) =>
{
if (data == "inc")
{
self.num++;
self.slot("slot", self.num);
}
},
});
</script>
<h1>Clicker</h1>
<div>
You have clicked
<slot></slot>
times!
</div>
<button onclick="broadcast('inc')">
Click
</button>
The HTML file that wants to use this component would include the hearsay.js
script file, and use a <hear-say src="Clicker.html"></hear-say>
element to display it.
<html>
<head>
<script src="hearsay.js"></script>
</head>
<body>
<hear-say struct="Clicker.html"></hear-say>
</body>
</html>
More information and examples:
Also see my hearsay "build journal"
Top comments (0)