From eb706ad5c8268425325166e71cda5ce9aa45e424 Mon Sep 17 00:00:00 2001 From: nparashar150 Date: Thu, 6 Oct 2022 19:38:54 +0530 Subject: [PATCH] * Compare oldNode with newNode on renders * Create basic diffing function * Update attributes as per old and new Nodes * Update children as per old and new Nodes --- lib/render.js | 123 ++++++++++++++++++++++++++++++-- lib/useState.js | 14 ++++ public/index.html | 6 +- public/js/own-react-bundle.js | 2 +- public/js/useState.js | 4 +- src/components/button/button.js | 8 ++- src/global.css | 25 +++++++ src/index.js | 58 ++++++++++++++- 8 files changed, 228 insertions(+), 12 deletions(-) create mode 100644 lib/useState.js create mode 100644 src/global.css diff --git a/lib/render.js b/lib/render.js index e86829d..d31ce34 100644 --- a/lib/render.js +++ b/lib/render.js @@ -1,9 +1,124 @@ /** - * - * @param {HTMLElement} element - * @param {String} containerId + * + * @param {HTMLElement} element + * @param {String} containerId */ export const render = (element, containerId) => { const root = document.getElementById(containerId); - root.appendChild(element); + if (!root.firstChild) { + return root.appendChild(element); + } + const oldNode = root.firstChild; + const newNode = element; + + diff(oldNode, newNode); +}; + +/** + * + * @param {NodeList} oldNode + * @param {NodeList} newNode + */ +export const diff = (oldNode, newNode) => { + // Reference to nodeTypes: https://developer.mozilla.org/en-US/docs/Web/API/Node/nodeType + + // If the old node is a text node and the new node is a text node + if (oldNode.nodeType === 3 && newNode.nodeType === 3) { + // If the text content is different + if (oldNode.textContent !== newNode.textContent) { + // Update the text content + oldNode.textContent = newNode.textContent; + } + } + + // If the old node is a text node and the new node is not a text node + if (oldNode.nodeType === 3 && newNode.nodeType !== 3) { + // Replace the old node with the new node + oldNode.replaceWith(newNode); + } + + // If the old node is not a text node and the new node is a text node + if (oldNode.nodeType !== 3 && newNode.nodeType === 3) { + // Replace the old node with the new node + oldNode.replaceWith(newNode); + } + + // If the old node is not a text node and the new node is not a text node + if (oldNode.nodeType !== 3 && newNode.nodeType !== 3) { + // If the tag names are different + if (oldNode.tagName !== newNode.tagName) { + // Replace the old node with the new node + oldNode.replaceWith(newNode); + } + + // If the tag names are the same + if (oldNode.tagName === newNode.tagName) { + // Update the attributes + updateAttributes(oldNode, newNode); + + // Update the children + updateChildren(oldNode, newNode); + } + } +}; + +/** + * + * @param {NodeList} oldNode + * @param {NodeList} newNode + */ +export const updateAttributes = (oldNode, newNode) => { + // Reference to attributes: https://developer.mozilla.org/en-US/docs/Web/API/Element/attributes + + // Get the old attributes + const oldAttributes = Array.from(oldNode.attributes); + + // Get the new attributes + const newAttributes = Array.from(newNode.attributes); + + // Remove the old attributes + oldAttributes.forEach((attribute) => { + oldNode.removeAttribute(attribute.name); + }); + + // Add the new attributes + newAttributes.forEach((attribute) => { + oldNode.setAttribute(attribute.name, attribute.value); + }); +}; + +/** + * + * @param {NodeList} oldNode + * @param {NodeList} newNode + */ +export const updateChildren = (oldNode, newNode) => { + // Reference to childNodes: https://developer.mozilla.org/en-US/docs/Web/API/Node/childNodes + + // Get the old children + const oldChildren = Array.from(oldNode.childNodes); + + // Get the new children + const newChildren = Array.from(newNode.childNodes); + + // If the new node has more children, add them + newChildren.forEach((child, index) => { + if (index >= oldChildren.length) { + oldNode.appendChild(child); + } + }); + + // If the new node has less children, remove them + oldChildren.forEach((child, index) => { + if (index >= newChildren.length) { + oldNode.removeChild(child); + } + }); + + // If the new node has the same number of children, update them + oldChildren.forEach((child, index) => { + if (index < newChildren.length) { + diff(child, newChildren[index]); + } + }); }; diff --git a/lib/useState.js b/lib/useState.js new file mode 100644 index 0000000..355600b --- /dev/null +++ b/lib/useState.js @@ -0,0 +1,14 @@ +/** + * +// * @param {*} initialState + * @param {*} _state + */ +export const useState = (initialState) => { + let _state = initialState; + const _setState = (newState) => { + if (_state === newState) return; + _state = newState; + return _state; + }; + return [_state, _setState]; +}; diff --git a/public/index.html b/public/index.html index 4041c25..ad1ecf1 100644 --- a/public/index.html +++ b/public/index.html @@ -1,5 +1,6 @@ + @@ -7,8 +8,11 @@ Document - + + +
+ \ No newline at end of file diff --git a/public/js/own-react-bundle.js b/public/js/own-react-bundle.js index c750f41..69f895c 100644 --- a/public/js/own-react-bundle.js +++ b/public/js/own-react-bundle.js @@ -1 +1 @@ -(()=>{"use strict";var t=function(t){return 0===Object.keys(t).length?"":Object.keys(t).reduce((function(e,n){return e+n.split(/(?=[A-Z])/).join("-").toLowerCase()+":"+t[n]+";"}),"")};function e(t){return e="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(t){return typeof t}:function(t){return t&&"function"==typeof Symbol&&t.constructor===Symbol&&t!==Symbol.prototype?"symbol":typeof t},e(t)}const n=function(n){var o,l,i=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{},r="string"==typeof n?null===(o=document)||void 0===o?void 0:o.createElement(n):n();i&&"object"===e(i)&&Object.keys(i).length>0&&(null===(l=Object.keys(i))||void 0===l||l.map((function(e){e.includes("on")?r.addEventListener(e.slice(2),i[e]):"classname"===e.toLowerCase()?r.setAttribute("class",i[e]):"style"===e.toLowerCase()?r.setAttribute("style",t(i[e])):null==r||r.setAttribute(e,i[e])})));for(var u=arguments.length,s=new Array(u>2?u-2:0),c=2;c{"use strict";var e={958:(e,n,t)=>{t.d(n,{Z:()=>l});var o=t(81),r=t.n(o),a=t(645),i=t.n(a)()(r());i.push([e.id,".__btn {\n cursor: pointer;\n font-weight: 600;\n font-size: 1.25rem;\n outline-offset: 1px;\n padding: 1.25rem 3rem;\n border-radius: 0.5rem;\n border: 1px solid #1a1a1a;\n outline: 1px solid transparent;\n transition: all 0.375s ease-in-out;\n}\n\n.__btn:hover,\n.__btn:focus {\n color: #fafafa;\n outline-offset: 1px;\n border: 1px solid #fafafa;\n background-color: #1a1a1a;\n outline: 1px solid #1a1a1a;\n}\n",""]);const l=i},767:(e,n,t)=>{t.d(n,{Z:()=>l});var o=t(81),r=t.n(o),a=t(645),i=t.n(a)()(r());i.push([e.id,"*,\nhtml,\nbody {\n margin: 0;\n padding: 0;\n box-sizing: border-box;\n font-family: monospace;\n}\n\nhtml,\nbody {\n padding: 1rem;\n}\n\nhtml,\nbody,\nh1,\nh2,\nh3,\nh4,\nh5,\nh6,\np {\n padding-bottom: 1rem;\n}\n",""]);const l=i},645:e=>{e.exports=function(e){var n=[];return n.toString=function(){return this.map((function(n){var t="",o=void 0!==n[5];return n[4]&&(t+="@supports (".concat(n[4],") {")),n[2]&&(t+="@media ".concat(n[2]," {")),o&&(t+="@layer".concat(n[5].length>0?" ".concat(n[5]):""," {")),t+=e(n),o&&(t+="}"),n[2]&&(t+="}"),n[4]&&(t+="}"),t})).join("")},n.i=function(e,t,o,r,a){"string"==typeof e&&(e=[[null,e,void 0]]);var i={};if(o)for(var l=0;l0?" ".concat(u[5]):""," {").concat(u[1],"}")),u[5]=a),t&&(u[2]?(u[1]="@media ".concat(u[2]," {").concat(u[1],"}"),u[2]=t):u[2]=t),r&&(u[4]?(u[1]="@supports (".concat(u[4],") {").concat(u[1],"}"),u[4]=r):u[4]="".concat(r)),n.push(u))}},n}},81:e=>{e.exports=function(e){return e[1]}},379:e=>{var n=[];function t(e){for(var t=-1,o=0;o{var n={};e.exports=function(e,t){var o=function(e){if(void 0===n[e]){var t=document.querySelector(e);if(window.HTMLIFrameElement&&t instanceof window.HTMLIFrameElement)try{t=t.contentDocument.head}catch(e){t=null}n[e]=t}return n[e]}(e);if(!o)throw new Error("Couldn't find a style target. This probably means that the value for the 'insert' parameter is invalid.");o.appendChild(t)}},216:e=>{e.exports=function(e){var n=document.createElement("style");return e.setAttributes(n,e.attributes),e.insert(n,e.options),n}},565:(e,n,t)=>{e.exports=function(e){var n=t.nc;n&&e.setAttribute("nonce",n)}},795:e=>{e.exports=function(e){var n=e.insertStyleElement(e);return{update:function(t){!function(e,n,t){var o="";t.supports&&(o+="@supports (".concat(t.supports,") {")),t.media&&(o+="@media ".concat(t.media," {"));var r=void 0!==t.layer;r&&(o+="@layer".concat(t.layer.length>0?" ".concat(t.layer):""," {")),o+=t.css,r&&(o+="}"),t.media&&(o+="}"),t.supports&&(o+="}");var a=t.sourceMap;a&&"undefined"!=typeof btoa&&(o+="\n/*# sourceMappingURL=data:application/json;base64,".concat(btoa(unescape(encodeURIComponent(JSON.stringify(a))))," */")),n.styleTagTransform(o,e,n.options)}(n,e,t)},remove:function(){!function(e){if(null===e.parentNode)return!1;e.parentNode.removeChild(e)}(n)}}}},589:e=>{e.exports=function(e,n){if(n.styleSheet)n.styleSheet.cssText=e;else{for(;n.firstChild;)n.removeChild(n.firstChild);n.appendChild(document.createTextNode(e))}}}},n={};function t(o){var r=n[o];if(void 0!==r)return r.exports;var a=n[o]={id:o,exports:{}};return e[o](a,a.exports,t),a.exports}t.n=e=>{var n=e&&e.__esModule?()=>e.default:()=>e;return t.d(n,{a:n}),n},t.d=(e,n)=>{for(var o in n)t.o(n,o)&&!t.o(e,o)&&Object.defineProperty(e,o,{enumerable:!0,get:n[o]})},t.o=(e,n)=>Object.prototype.hasOwnProperty.call(e,n),t.nc=void 0,(()=>{var e=function(e){return 0===Object.keys(e).length?"":Object.keys(e).reduce((function(n,t){return n+t.split(/(?=[A-Z])/).join("-").toLowerCase()+":"+e[t]+";"}),"")};function n(e){return n="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},n(e)}var o=function(e,n){3===e.nodeType&&3===n.nodeType&&e.textContent!==n.textContent&&(e.textContent=n.textContent),3===e.nodeType&&3!==n.nodeType&&e.replaceWith(n),3!==e.nodeType&&3===n.nodeType&&e.replaceWith(n),3!==e.nodeType&&3!==n.nodeType&&(e.tagName!==n.tagName&&e.replaceWith(n),e.tagName===n.tagName&&(r(e,n),a(e,n)))},r=function(e,n){var t=Array.from(e.attributes),o=Array.from(n.attributes);t.forEach((function(n){e.removeAttribute(n.name)})),o.forEach((function(n){e.setAttribute(n.name,n.value)}))},a=function(e,n){var t=Array.from(e.childNodes),r=Array.from(n.childNodes);r.forEach((function(n,o){o>=t.length&&e.appendChild(n)})),t.forEach((function(n,t){t>=r.length&&e.removeChild(n)})),t.forEach((function(e,n){n1&&void 0!==arguments[1]?arguments[1]:{},i="string"==typeof t?null===(o=document)||void 0===o?void 0:o.createElement(t):t();a&&"object"===n(a)&&Object.keys(a).length>0&&(null===(r=Object.keys(a))||void 0===r||r.map((function(n){n.includes("on")?i.addEventListener(n.slice(2).toLowerCase(),a[n]):"classname"===n.toLowerCase()?i.setAttribute("class",a[n]):"style"===n.toLowerCase()?i.setAttribute("style",e(a[n])):null==i||i.setAttribute(n,a[n])})));for(var l=arguments.length,s=new Array(l>2?l-2:0),c=2;c { const handleClick = () => { - alert("Button clicked"); + console.log("Button clicked"); }; - return ; + return ( + + ); }; export default Button; diff --git a/src/global.css b/src/global.css new file mode 100644 index 0000000..37ae347 --- /dev/null +++ b/src/global.css @@ -0,0 +1,25 @@ +*, +html, +body { + margin: 0; + padding: 0; + box-sizing: border-box; + font-family: monospace; +} + +html, +body { + padding: 1rem; +} + +html, +body, +h1, +h2, +h3, +h4, +h5, +h6, +p { + padding-bottom: 1rem; +} diff --git a/src/index.js b/src/index.js index 89ab213..ea06821 100644 --- a/src/index.js +++ b/src/index.js @@ -1,18 +1,72 @@ import OwnReact from "../lib/index"; import Button from "./components/button/button"; +import "./global.css"; const App = () => { return (
-

My First Own React App

My First Own React App with custom Babel and Webpack config

This is another nesting level

+

); }; -OwnReact.render(App(), "root"); +const App2 = () => { + return ( +
+

My Second Own React App

+

+

+

My Second Own React App with custom Babel and Webpack config

+

This is another nesting level

+

This is another nesting level

+

This is another nesting level

+ +
+

+
+ ); +}; + +const App3 = () => { + return ( +
+

My Third Own React App

+

+

+

My Third Own React App with custom Babel and Webpack config

+

This is another nesting level

+

This is another another nesting level

+ +
+

+
+ ); +}; + +const App4 = () => { + return ( +
+

My Fourth Own React App

+
+ ); +}; + +OwnReact.render(, "root"); + +setTimeout(() => { + OwnReact.render(, "root"); +}, 5000); + +setTimeout(() => { + OwnReact.render(, "root"); +}, 10000); + +setTimeout(() => { + OwnReact.render(, "root"); +}, 15000);