Skip to content

Instantly share code, notes, and snippets.

@thinkofher
Last active Dec 21, 2020
Embed
What would you like to do?
vanilla js minimal react clone based on valoo
window.act = (() => {
/*!
* Just the bare necessities of state management
* (c) 2018 Google LLC, Apache-2.0 License
*
* https://gist.github.com/developit/a0430c500f5559b715c2dddf9c40948d
*
* @param {Any} v initial value
* @param {Array.<Function>} cb array with callback functions
*/
const state = (v, cb) => {
cb = cb || [];
return function (c) {
if (c === void 0) return v;
if (c.call) return cb.splice.bind(cb, cb.push(c) - 1, 1, null);
v = c;
for (var i = 0, l = cb.length; i < l; i++) {
cb[i] && cb[i](v);
}
};
};
/*!
* Creates new HTMLElement with given tag, props and children.
*
* Implemented to use instead of HTML templates, because you can
* use regulra JavaScript to generate nodes.
*
* @param {String} tag Html tag like: div, p, b etc
* @param {Object} props Dictionary with properties for new element
* @param {Array.<HTMLElement|String>} children
* @return {HTMLElement} Created node
*/
const el = (tag, props, ...children) => {
if (typeof tag === "undefined") return false;
// Pass empty string if children is undefined.
if (typeof children === "undefined") children = [""];
const result = document.createElement(tag);
if (typeof props === "object") {
for (const key in props) {
let eventName = key.match(/^on([A-Z]\w+)$/);
// If key matches some event name, add event listener with given function
// for matched event.
if (eventName) {
result.addEventListener(eventName[1].toLowerCase(), props[key]);
} // Otherwise set regular attribute.
else if (key === "style" && typeof props[key] === "object") {
for (const trait in props[key]) {
result.style[trait] = props[key][trait];
}
} else if (key === "className") {
result.className = props[key];
}
else {
result.setAttribute(key, props[key]);
}
}
}
// For each child, add to new element. If child is not
// HTMLElement, create new text node.
children.forEach((child) => {
result.appendChild(
child instanceof HTMLElement ? child : document.createTextNode(child),
);
});
return result;
};
/*!
* Render replaces given target HTMLElement with new HTMLElement.
*
* @param {HTMLElement} target node
* @param {HTMLElement} element new node to render
* @return {HTMLElement} old html node.
*/
const render = (target, element) => {
return target.parentNode.replaceChild(element, target);
};
return { state, el, render };
})();
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<title>App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<script src="act.js"></script>
<script src="index.js"></script>
</body>
</html>
(() => {
const { el, render, state } = act;
const Counter = ({ title, color, initialState }) => {
const counterState = state(initialState);
const setupOutput = (newValue) =>
'Counter "' + title + '" value is ' + newValue + ".";
const output = el(
"h1",
{ style: { color: color } },
setupOutput(initialState),
);
const incr = () => counterState(counterState() + 1);
const decr = () => counterState(counterState() - 1);
const reset = () => counterState(initialState);
counterState((newState) => {
output.innerText = setupOutput(newState);
});
return el(
"div",
null,
output,
el("button", { onClick: incr }, "+"),
el("button", { onClick: reset }, "reset"),
el("button", { onClick: decr }, "-"),
);
};
const App = () => {
return el(
"div",
null,
Counter({ title: "A", color: "red", initialState: 0 }),
Counter({ title: "B", color: "green", initialState: -5 }),
Counter({ title: "C", color: "blue", initialState: 5 }),
);
};
render(document.getElementById("root"), App());
})();
@thinkofher
Copy link
Author

thinkofher commented Dec 21, 2020

Example usage here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment