Skip to content

Instantly share code, notes, and snippets.

@lmuntaner
Last active June 26, 2023 12:03
Show Gist options
  • Save lmuntaner/ceb1fec7e78f891b26e692a7902b1e6f to your computer and use it in GitHub Desktop.
Save lmuntaner/ceb1fec7e78f891b26e692a7902b1e6f to your computer and use it in GitHub Desktop.
Building a React-Like Library - Part III: useState
const createElement = (tag, props, children ) => ({ tag, props, children });
const hooks = [];
let useStateCalls = -1;
const useState = (initialState) => {
useStateCalls += 1;
// Each hook is an array with two elements:
// - hook[0] is the state
// - hook[1] is the setState function
if (hooks[useStateCalls] === undefined) {
const hook = [initialState]
hooks[useStateCalls] = hook;
hook[1] = (updatedState) => {
console.log('in da setState');
hook[0] = updatedState;
renderApp();
}
}
return hooks[useStateCalls];
}
const render = (vnode, parent) => {
if (typeof vnode === "string" || typeof vnode === "number") {
return parent.appendChild(document.createTextNode(vnode));
}
if (typeof vnode.tag === "function") {
const nextVNode = vnode.tag(vnode.props);
return render(nextVNode, parent);
}
const element = document.createElement(vnode.tag);
if (vnode.props) {
Object.keys(vnode.props).forEach(key => {
const value = vnode.props[key];
// Add event listeners from props `on<Event>`
if (key.startsWith("on")) {
const event = key.slice(2).toLowerCase();
element.addEventListener(event, value);
} else {
element.setAttribute(key, value);
}
});
}
if (vnode.children) {
vnode.children.forEach(child => render(child, element));
}
return parent.appendChild(element);
};
const Title = () => createElement("h1", {}, ["Hello from dynamic app!"]);
const Counter = () => {
const [count, setCount] = useState(0);
const increment = () => {
setCount(count + 1);
};
const decrement = () => {
setCount(count - 1);
};
return createElement("div", {}, [
createElement("button", { onClick: increment }, ["+"]),
createElement("h3", {}, [count]),
createElement("button", { onClick: decrement }, ["-"]),
])
}
const createApp2 = () => createElement(
"div",
{},
[
createElement(Title, {}, []),
createElement(Counter, {}, []),
createElement("p", {}, ["This was build with `createElement`"])
]
);
const renderApp = () => {
useStateCalls = -1;
const parent = document.getElementById("root");
parent.innerHTML = "";
render(createApp2(), parent);
};
renderApp();
<!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>Document</title>
</head>
<body>
<div id="root"></div>
<script src="./app-use-state.js"></script> -->
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment