Repository: pomber/didact Branch: master Commit: bb72345b2300 Files: 4 Total size: 12.7 KB Directory structure: gitextract_3ia6oj2f/ ├── .prettierrc ├── didact.js ├── package.json └── readme.md ================================================ FILE CONTENTS ================================================ ================================================ FILE: .prettierrc ================================================ { "semi": false, "printWidth": 52, "trailingComma": "es5" } ================================================ FILE: didact.js ================================================ function createElement(type, props, ...children) { return { type, props: { ...props, children: children.map(child => typeof child === "object" ? child : createTextElement(child) ), }, } } function createTextElement(text) { return { type: "TEXT_ELEMENT", props: { nodeValue: text, children: [], }, } } function createDom(fiber) { const dom = fiber.type == "TEXT_ELEMENT" ? document.createTextNode("") : document.createElement(fiber.type) updateDom(dom, {}, fiber.props) return dom } const isEvent = key => key.startsWith("on") const isProperty = key => key !== "children" && !isEvent(key) const isNew = (prev, next) => key => prev[key] !== next[key] const isGone = (prev, next) => key => !(key in next) function updateDom(dom, prevProps, nextProps) { //Remove old or changed event listeners Object.keys(prevProps) .filter(isEvent) .filter( key => !(key in nextProps) || isNew(prevProps, nextProps)(key) ) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.removeEventListener( eventType, prevProps[name] ) }) // Remove old properties Object.keys(prevProps) .filter(isProperty) .filter(isGone(prevProps, nextProps)) .forEach(name => { dom[name] = "" }) // Set new or changed properties Object.keys(nextProps) .filter(isProperty) .filter(isNew(prevProps, nextProps)) .forEach(name => { dom[name] = nextProps[name] }) // Add event listeners Object.keys(nextProps) .filter(isEvent) .filter(isNew(prevProps, nextProps)) .forEach(name => { const eventType = name .toLowerCase() .substring(2) dom.addEventListener( eventType, nextProps[name] ) }) } function commitRoot() { deletions.forEach(commitWork) commitWork(wipRoot.child) currentRoot = wipRoot wipRoot = null } function commitWork(fiber) { if (!fiber) { return } let domParentFiber = fiber.parent while (!domParentFiber.dom) { domParentFiber = domParentFiber.parent } const domParent = domParentFiber.dom if ( fiber.effectTag === "PLACEMENT" && fiber.dom != null ) { domParent.appendChild(fiber.dom) } else if ( fiber.effectTag === "UPDATE" && fiber.dom != null ) { updateDom( fiber.dom, fiber.alternate.props, fiber.props ) } else if (fiber.effectTag === "DELETION") { commitDeletion(fiber, domParent) } commitWork(fiber.child) commitWork(fiber.sibling) } function commitDeletion(fiber, domParent) { if (fiber.dom) { domParent.removeChild(fiber.dom) } else { commitDeletion(fiber.child, domParent) } } function render(element, container) { wipRoot = { dom: container, props: { children: [element], }, alternate: currentRoot, } deletions = [] nextUnitOfWork = wipRoot } let nextUnitOfWork = null let currentRoot = null let wipRoot = null let deletions = null function workLoop(deadline) { let shouldYield = false while (nextUnitOfWork && !shouldYield) { nextUnitOfWork = performUnitOfWork( nextUnitOfWork ) shouldYield = deadline.timeRemaining() < 1 } if (!nextUnitOfWork && wipRoot) { commitRoot() } requestIdleCallback(workLoop) } requestIdleCallback(workLoop) function performUnitOfWork(fiber) { const isFunctionComponent = fiber.type instanceof Function if (isFunctionComponent) { updateFunctionComponent(fiber) } else { updateHostComponent(fiber) } if (fiber.child) { return fiber.child } let nextFiber = fiber while (nextFiber) { if (nextFiber.sibling) { return nextFiber.sibling } nextFiber = nextFiber.parent } } let wipFiber = null let hookIndex = null function updateFunctionComponent(fiber) { wipFiber = fiber hookIndex = 0 wipFiber.hooks = [] const children = [fiber.type(fiber.props)] reconcileChildren(fiber, children) } function useState(initial) { const oldHook = wipFiber.alternate && wipFiber.alternate.hooks && wipFiber.alternate.hooks[hookIndex] const hook = { state: oldHook ? oldHook.state : initial, queue: [], } const actions = oldHook ? oldHook.queue : [] actions.forEach(action => { hook.state = action(hook.state) }) const setState = action => { hook.queue.push(action) wipRoot = { dom: currentRoot.dom, props: currentRoot.props, alternate: currentRoot, } nextUnitOfWork = wipRoot deletions = [] } wipFiber.hooks.push(hook) hookIndex++ return [hook.state, setState] } function updateHostComponent(fiber) { if (!fiber.dom) { fiber.dom = createDom(fiber) } reconcileChildren(fiber, fiber.props.children) } function reconcileChildren(wipFiber, elements) { let index = 0 let oldFiber = wipFiber.alternate && wipFiber.alternate.child let prevSibling = null while ( index < elements.length || oldFiber != null ) { const element = elements[index] let newFiber = null const sameType = oldFiber && element && element.type == oldFiber.type if (sameType) { newFiber = { type: oldFiber.type, props: element.props, dom: oldFiber.dom, parent: wipFiber, alternate: oldFiber, effectTag: "UPDATE", } } if (element && !sameType) { newFiber = { type: element.type, props: element.props, dom: null, parent: wipFiber, alternate: null, effectTag: "PLACEMENT", } } if (oldFiber && !sameType) { oldFiber.effectTag = "DELETION" deletions.push(oldFiber) } if (oldFiber) { oldFiber = oldFiber.sibling } if (index === 0) { wipFiber.child = newFiber } else if (element) { prevSibling.sibling = newFiber } prevSibling = newFiber index++ } } const Didact = { createElement, render, useState, } /** @jsx Didact.createElement */ function Counter() { const [state, setState] = Didact.useState(1) return (

setState(c => c + 1)}> Count: {state}

) } const element = const container = document.getElementById("root") Didact.render(element, container) ================================================ FILE: package.json ================================================ { "name": "didact", "version": "3.0.0", "description": "A didactic alternative to React", "license": "MIT", "repository": "pomber/didact", "author": "pomber", "main": "didact.js", "keywords": [ "react" ] } ================================================ FILE: readme.md ================================================

# Didact #### A DIY guide to build your own React This repository goes together with a [series of posts](https://medium.com/hexacta-engineering/didact-learning-how-react-works-by-building-it-from-scratch-51007984e5c5) that explains how to build React from scratch step by step. **You can jump straight to [the last post](https://pomb.us/build-your-own-react) which is self-contained and includes everything.** | Blog Post | Code sample | Commits | Other languages | | ----------------------------------------------------------------------------------------------------------------------------------------------- | :----------------------------------------------------------: | :---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------: | :-------------------------------------------------------------------------------------------------------------------------: | | [Introduction](https://medium.com/hexacta-engineering/didact-learning-how-react-works-by-building-it-from-scratch-51007984e5c5) | | | | | [Rendering DOM elements](https://medium.com/hexacta-engineering/didact-rendering-dom-elements-91c9aa08323b) | [codepen](https://codepen.io/pomber/pen/eWbwBq?editors=0010) | [diff](https://github.com/hexacta/didact/commit/fc4d360d91a1e68f0442d39dbce5b9cca5a08f24) | [中文](https://github.com/chinanf-boy/didact-explain#1-%E6%B8%B2%E6%9F%93dom%E5%85%83%E7%B4%A0) | | [Element creation and JSX](https://medium.com/hexacta-engineering/didact-element-creation-and-jsx-d05171c55c56) | [codepen](https://codepen.io/pomber/pen/xdmoWE?editors=0010) | [diff](https://github.com/hexacta/didact/commit/15010f8e7b8b54841d1e2dd9eacf7b3c06b1a24b) | [中文](https://github.com/chinanf-boy/didact-explain#2-%E5%85%83%E7%B4%A0%E5%88%9B%E5%BB%BA%E5%92%8Cjsx) | | [Virtual DOM and reconciliation](https://medium.com/hexacta-engineering/didact-instances-reconciliation-and-virtual-dom-9316d650f1d0) | [codepen](https://codepen.io/pomber/pen/WjLqYW?editors=0010) | [diff](https://github.com/hexacta/didact/commit/8eb7ffd6f5e210526fb4c274c4f60d609fe2f810) [diff](https://github.com/hexacta/didact/commit/6f5fdb7331ed77ba497fa5917d920eafe1f4c8dc) [diff](https://github.com/hexacta/didact/commit/35619a039d48171a6e6c53bd433ed049f2d718cb) | [中文](https://github.com/chinanf-boy/didact-explain#3-%E5%AE%9E%E4%BE%8B-%E5%AF%B9%E6%AF%94%E5%92%8C%E8%99%9A%E6%8B%9Fdom) | | [Components and State](https://medium.com/hexacta-engineering/didact-components-and-state-53ab4c900e37) | [codepen](https://codepen.io/pomber/pen/RVqBrx) | [diff](https://github.com/hexacta/didact/commit/2e290ff5c486b8a3f361abcbc6e36e2c21db30b8) | [中文](https://github.com/chinanf-boy/didact-explain#4-%E7%BB%84%E4%BB%B6%E5%92%8C%E7%8A%B6%E6%80%81) | | [Fiber: Incremental reconciliation](https://medium.com/hexacta-engineering/didact-fiber-incremental-reconciliation-b2fe028dcaec) (self-contained post) | [codepen](https://codepen.io/pomber/pen/veVOdd) | [diff](https://github.com/hexacta/didact/commit/6174a2289e69895acd8fc85abdc3aaff1ded9011) [diff](https://github.com/hexacta/didact/commit/accafb81e116a0569f8b7d70e5b233e14af999ad) | [中文](https://github.com/chinanf-boy/didact-explain#5-fibre-%E9%80%92%E5%A2%9E%E5%AF%B9%E6%AF%94) | | [The one with Hooks](https://pomb.us/build-your-own-react) (self-contained post) | [codesandbox](https://codesandbox.io/s/didact-8-21ost) | | [中文](https://www.tangdingblog.cn/blog/react/buildyourownreact-2020-09-22/) | > Follow [@pomber](https://twitter.com/pomber) on twitter for updates. ## License The MIT License (MIT)