Last active
June 26, 2023 12:03
-
-
Save lmuntaner/ceb1fec7e78f891b26e692a7902b1e6f to your computer and use it in GitHub Desktop.
Building a React-Like Library - Part III: useState
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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(); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!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