UNPKG

mini-react-dom

Version:

A lightweight React-like DOM renderer with JSX, hooks, context, SSR and lazy support

102 lines (80 loc) 2.7 kB
import { resetHooks, setRerender } from "./hooks.js"; import { Fragment } from "./createElement.js"; import { reconcile } from "./reconcile.js"; let rootElement = null; let rootContainer = null; let prevVNode = null; export function render(element, container) { rootElement = element; rootContainer = container; resetHooks(); setRerender(() => render(rootElement, rootContainer)); if (prevVNode) { reconcile(container, prevVNode, element); } else { const dom = createDom(element); container.appendChild(dom); } prevVNode = element; } export function createDom(element) { if (typeof element.type === "function") { return createDom(element.type(element.props)); } if (element.type === Fragment) { const fragment = document.createDocumentFragment(); (element.props.children || []).forEach((child) => { fragment.appendChild(createDom(child)); }); return fragment; } const dom = element.type === "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(element.type); const isListener = (name) => name.startsWith("on"); const isAttribute = (name) => !isListener(name) && name !== "children"; Object.keys(element.props || {}).forEach((name) => { if (isListener(name)) { const eventType = name.toLowerCase().substring(2); dom.addEventListener(eventType, element.props[name]); } else if (isAttribute(name)) { dom[name] = element.props[name]; } }); (element.props?.children || []).forEach((child) => { dom.appendChild(createDom(child)); }); return dom; } export function hydrateDom(dom, element) { if (typeof element.type === "function") { return hydrateDom(dom, element.type(element.props)); } if (element.type === "TEXT_ELEMENT") { return; } const isListener = (name) => name.startsWith("on"); const isAttribute = (name) => !isListener(name) && name !== "children"; Object.keys(element.props || {}).forEach((name) => { if (isListener(name)) { const eventType = name.toLowerCase().substring(2); dom.addEventListener(eventType, element.props[name]); } else if (isAttribute(name)) { // optional: check against existing attributes if needed dom[name] = element.props[name]; } }); const children = element.props?.children || []; const domChildren = Array.from(dom.childNodes); for (let i = 0; i < children.length; i++) { hydrateDom(domChildren[i], children[i]); } } export function hydrate(element, container) { rootElement = element; rootContainer = container; resetHooks(); setRerender(() => render(rootElement, rootContainer)); hydrateDom(container, element); }