[{"data":1,"prerenderedAt":452},["ShallowReactive",2],{"blog-tag-javascript":3},[4],{"id":5,"title":6,"body":7,"date":438,"description":428,"extension":439,"featuredImage":440,"featuredImageAlt":428,"keywords":441,"meta":442,"navigation":443,"path":444,"seo":445,"stem":446,"subtitle":447,"tags":448,"__hash__":451},"blog\u002Fblog\u002Femulating-component-behavior-with-javascript-closures.md","Emulating Component Behavior with JavaScript Closures",{"type":8,"value":9,"toc":427},"minimark",[10,15,27,42,45,52,56,63,101,104,111,115,126,131,169,191,195,198,202,219,236,244,251,255,273,277,280,337,344,348,352,355,362,365,369,372,397,400,405,408,416,424],[11,12,14],"h2",{"id":13},"introduction","Introduction",[16,17,18,19,26],"p",{},"Recently, in an effort to level-up my advanced JavaScript knowledge, I've been diving into the \"wonderful\" world of Closures. If you've done any research on how Closures work in JavaScript for yourself, I'm sure you understand that it can be very confusing at first. For reference, here is the definition of a Closure from ",[20,21,25],"a",{"href":22,"rel":23},"https:\u002F\u002Fdeveloper.mozilla.org\u002Fen-US\u002Fdocs\u002FWeb\u002FJavaScript\u002FClosures",[24],"nofollow","the MDN Docs",":",[28,29,30],"blockquote",{},[16,31,32,33,37,38,41],{},"A ",[34,35,36],"strong",{},"closure"," is the combination of a function bundled together (enclosed) with references to its surrounding state (the ",[34,39,40],{},"lexical environment","). In other words, a closure gives you access to an outer function’s scope from an inner function. In JavaScript, closures are created every time a function is created, at function creation time.",[16,43,44],{},"Got it? Me neither.",[16,46,47,48,51],{},"I think an easier way of understanding Closures is to think of them as a way of manipulating the way variable scoping works in JavaScript to create the effect of ",[34,49,50],{},"private variables or functions",". Take this code snippet for example:",[11,53,55],{"id":54},"example","Example",[57,58,62],"code-block",{"lang":59,"filename":60,"highlight-lines":61},"js","Closure.js","7","\nfunction makeCalculator(x) {\n  return function add(y) {\n    return x + y\n  }\n}\n \nconst calculateFive = makeCalculator(5);\n \nconsole.log(calculateFive(4))\n\u002F\u002F Prints \"9\"\nconsole.log(calculateFive.add(4))\n\u002F\u002F TypeError - calculateFive.add is not a function\n",[16,64,65,66,70,71,74,75,78,79,81,82,85,86,89,90,92,93,96,97,100],{},"So we now have a function called ",[67,68,69],"code",{},"makeCalculator"," that takes a base value ",[67,72,73],{},"x",", and returns a function that takes another parameter ",[67,76,77],{},"y"," and adds it to ",[67,80,73],{},". Note that, while the function ",[67,83,84],{},"add(y)"," exists in the function block of ",[67,87,88],{},"makeCalculator(x)",", it is not accessible by any functions created with ",[67,91,69],{},". Basically, we have a Calculator component generator that ",[34,94,95],{},"obfuscates its addition logic"," from everything else, but is still ",[34,98,99],{},"clear in what it does"," from an outside perspective.",[16,102,103],{},"So, this is cool and all, but how can we actually use Closures in a practical application?",[16,105,106,107,110],{},"After thinking about this unique behavior for a while (talking to my cat about it), I figured out that we can ",[34,108,109],{},"emulate the behavior of Components",", similar to frontend JavaScript frameworks like React, using this function pattern! Allow me to walk you through my thought process.",[11,112,114],{"id":113},"initializing-a-component","Initializing a Component",[16,116,117,118,120,121,125],{},"The first thing we need to do is write a function that, similar to ",[67,119,69],{},", contains some data or functions related to our component, and returns a subset of those. And the first thing that we do with any variable, Class object, or component is ",[122,123,124],"em",{},"initialize it."," So lets write a function that does just that:",[57,127,130],{"lang":59,"filename":128,"highlight-lines":129},"Component.js","16","\nconst createComponent = function(props) {\n  const propsMap = new Map();\n \n  const render = (props) => {\n    \u002F\u002F Some rendering logic\n  }\n \n  return {\n    init() {\n      \u002F\u002F Some initialization stuff\n    }\n  }\n}\n \n\u002F\u002F NewComponent is assigned the value returned by createComponent\nconst NewComponent = createComponent({ name: 'Dan' })\n \nNewComponent.init()\nNewComponent.render() \u002F\u002F This will result in a TypeError!\n",[16,132,133,134,137,138,141,142,144,145,148,149,151,152,154,155,157,158,160,161,164,165,168],{},"Now I've created the function ",[67,135,136],{},"createComponent",", which returns a set of functions (for now, just ",[67,139,140],{},"init()","), and also includes some constants that are accessible only to the functions executed inside the scope of ",[67,143,136],{},". Then, I define a new variable ",[67,146,147],{},"NewComponent"," that is assigned the return value of ",[67,150,136],{},", which will be an object containing ",[67,153,140],{},". After ",[67,156,147],{}," is initialized, I can call the ",[67,159,140],{}," function from it, but I won't be able to directly access the values of ",[67,162,163],{},"propsMap"," and ",[67,166,167],{},"render",".",[16,170,171,172,175,176,179,180,182,183,186,187,190],{},"So we got something going here, but it doesn't really ",[122,173,174],{},"do anything yet",". What I ",[122,177,178],{},"want it to do"," is, on calling ",[67,181,140],{},", to ",[34,184,185],{},"render a new HTML element"," as a child of some other element, which in this case will be a ",[67,188,189],{},"\u003Cdiv>"," with an ID of \"app\". I also want those props I'm passing to be used in some way in that render.",[11,192,194],{"id":193},"creating-a-new-dom-element","Creating a New DOM Element",[16,196,197],{},"Here's how I accomplished this task:",[57,199,201],{"lang":59,"filename":128,"highlight-lines":200},"6-15","\nconst createComponent = function(props) {\n  \u002F\u002F Assign props value to propsMap so we can properly update them later\n  var propsMap = new Map(Object.entries(props));\n \n  const render = () => {\n    var name = propsMap.get('name') || ''\n    var app = document.getElementById(\"app\");\n    var el = document.createElement(\"div\");\n \n    \u002F\u002F Assign an ID so I can reference it somewhere else\n    el.id = \"new-component\";\n    el.innerHTML = name;\n \n    \u002F\u002F Attach the new element to the root \"app\" node\n    app.appendChild(el);\n  };\n \n  return {\n    init() {\n      render(props); \u002F\u002F Called on `init`, but still not available to NewComponent\n    }\n  };\n};\n \n\u002F\u002F Create two independent components\nconst ComponentOne = createComponent({ name: 'Dan' })\nComponentOne.init()\n \nconst ComponentTwo = createComponent({ name: 'John' })\nComponentOne.init()\n",[16,203,204,205,207,208,211,212,215,216,218],{},"Now I've fleshed out the ",[67,206,167],{}," function to actually create a new element in the DOM, and gave it an ID value so that we can reference it later through the ",[67,209,210],{},"document"," object. I also created an additional ",[67,213,214],{},"Map"," object named ",[67,217,163],{}," so that we can mutate props freely later on.",[16,220,221,222,224,225,227,228,231,232,235],{},"The meat of the ",[67,223,167],{}," function is that it's getting the app element as a mounting point, creating a new ",[67,226,189],{}," element with a unique ID, and set its ",[67,229,230],{},"innerHTML"," to the name string we pass in as props. Now, the result of running ",[67,233,234],{},"NewComponent.init()"," is shown below:",[237,238],"nuxt-picture",{"format":239,"alt":240,"src":241,"width":242,"loading":243},"webp","Screenshot of the result of the current createComponent function","\u002Fimg\u002Fclosure-component-1.png","400px","lazy",[16,245,246,247,250],{},"The result is that we have two independent components that render in the DOM, and are unaffected by each other. ",[122,248,249],{},"Absolutely bonkers!!!"," Nothing really crazy yet, but it's a start.",[11,252,254],{"id":253},"make-em-dynamic","Make 'em Dynamic",[16,256,257,258,261,262,264,265,268,269,272],{},"The next step is to make our components dynamic, so that when we change the value of ",[67,259,260],{},"props"," the DOM element created by ",[67,263,167],{}," will be updated accordingly. So we'll need another function available publically through ",[67,266,267],{},"ComponentOne"," or ",[67,270,271],{},"ComponentTwo"," so that we can update those props and trigger a re-render. We're also going to check the DOM before rendering to see if the component node already exists, to avoid duplicate nodes.",[57,274,276],{"lang":59,"filename":128,"highlight-lines":275},"6-9,17,19,50-52","\nconst createComponent = function (props) {\n  var propsMap = new Map();\n \n  \u002F\u002F Allows us to mutate props freely\n  const setPropValues = (p) => {\n    const propEntries = Object.entries(p);\n    propEntries.forEach(([key, value]) => {\n      propsMap.set(key, value);\n    });\n  };\n \n  \u002F\u002F Returns the current DOM node, or creates a new one and returns it.\n  const getRootNode = () => {\n    var id = propsMap.get(\"id\"); \u002F\u002F new prop for element IDs\n \n    \u002F\u002F Check if the element exists in the DOM\n    const existing = document.getElementById(id);\n \n    if (!existing) {\n      \u002F\u002F Append the root node to our app element\n      var app = document.getElementById(\"app\");\n      var el = document.createElement(\"div\");\n      el.id = id;\n \n      app.appendChild(el);\n \n      return el; \u002F\u002F Return the new element...\n    }\n \n    return existing; \u002F\u002F Otherwise, return the existing one.\n  };\n \n  const render = () => {\n    var name = propsMap.get(\"name\") || \"\";\n    var el = getRootNode();\n \n    el.innerHTML = name;\n  };\n \n  \u002F\u002F Update props to values from p and render in DOM\n  const renderWithProps = (p) => {\n    setPropValues(p);\n    render();\n  };\n \n  return {\n    init() {\n      renderWithProps(props); \u002F\u002F props from creatComponent()\n    },\n    update(newProps) {\n      renderWithProps(newProps); \u002F\u002F props from update()\n    }\n  };\n};\n",[16,278,279],{},"So, there's a few things going on here now.",[281,282,283,294,304,311,322],"ul",{},[284,285,286,287,290,291,293],"li",{},"There's a new function ",[67,288,289],{},"setPropValues()"," that updates the ",[67,292,163],{}," Map object with whatever values we provide it.",[284,295,296,297,300,301,303],{},"Another new function named ",[67,298,299],{},"renderWithProps()",", that assigns prop values using ",[67,302,289],{}," and renders the component to the DOM.",[284,305,306,307,310],{},"Yet another new function ",[67,308,309],{},"getRootNode()",", which checks the DOM to see if the component has already been rendered, and otherwise creates and attaches a new node to the DOM. Then it returns either the new or existing node.",[284,312,313,314,317,318,321],{},"I updated the ",[67,315,316],{},"render()"," function to call ",[67,319,320],{},"getRootNode"," to avoid duplicate nodes being attached to the DOM when the component updates.",[284,323,324,325,328,329,332,333,336],{},"Last, I added the function ",[67,326,327],{},"update(newProps)"," to the return value of ",[67,330,331],{},"createComponent()",", which takes a parameter ",[67,334,335],{},"newProps"," and re-renders, making our component dynamic!",[16,338,339,340,343],{},"To test the encapsulation of props and behavior of rendering vs. updating these components, I added a couple of buttons to the DOM that call ",[67,341,342],{},"update({ ...newProps })"," on click:",[57,345,347],{"lang":59,"filename":128,"highlight-lines":346},"11-15","\nconst ComponentOne = createComponent({\n  id: \"component-one\", \u002F\u002F new prop\n  name: \"Dan\",\n});\nComponentOne.init();\n \n\u002F\u002F (ComponentTwo init code here)\n \nconst btnOne = document.createElement(\"button\");\nbtnOne.innerHTML = \"Update One\";\nbtnOne.addEventListener(\"click\", function () {\n  ComponentOne.update({\n    name: \"Peter\"\n  });\n});\n \n\u002F\u002F btnTwo code here that updates ComponentTwo\n",[11,349,351],{"id":350},"the-moment-of-truth","The Moment of Truth",[16,353,354],{},"Now, let's see if it actually works...",[16,356,357],{},[358,359],"img",{"alt":360,"src":361},"Screen recording of final result","\u002Fimg\u002Fclosure-component-result.gif",[16,363,364],{},"And it does! 🥳",[11,366,368],{"id":367},"how-closure-makes-this-work","How Closure Makes This Work",[16,370,371],{},"So, you might be thinking to yourself, \"Sure, you've got some functions and some variables and functions within those functions, but I don't understand how closures fit into this.\" Allow me to explain.",[16,373,374,375,377,378,164,380,382,383,386,387,390,391,164,393,396],{},"The Closure here exists in ",[67,376,136],{}," and the functions contained and returned by it. We have variables and functions, like ",[67,379,163],{},[67,381,299],{},", which are used in the internal logic of our Component, but are not accessible by any variables that are assigned the resulting value of the expression ",[67,384,385],{},"createComponent(props)",". We ",[122,388,389],{},"specifically"," allow only certain functions, such as ",[67,392,140],{},[67,394,395],{},"update()"," to be called outside the function. This behavior keeps the internal workings of the Component hidden from its callers, but still provides all the necessary interfaces to create a dynamic component.",[16,398,399],{},"The code snippet below, similar to the first example, demonstrates this distinction between our \"public\" and \"private\" values:",[57,401,404],{"lang":59,"filename":402,"highlight-lines":403},"Result.js","7,10-12,15","\nconst component = createComponent({\n  id: 'example',\n  name: 'Dan'\n})\n \n\u002F\u002F Renders a new node in the DOM with the text \"Dan\"\ncomponent.init()\n \n\u002F\u002F Updates the props of our component and updates the existing DOM node\ncomponent.update({\n  name: 'Peter'\n})\n \n\u002F\u002F Results in a TypeError - component.render is not a function!\ncomponent.render()\n",[16,406,407],{},"Our end result is essentially a super-watered down Virtual DOM API, but I think it serves as a good example for how a Closure can be implemented in JavaScript in a way that is both useful and clear in its implementation. I hope this tutorial has been helpful for you, whether you had no idea what a Closure was, or if you already knew about Closures but have never seen how it's used.",[16,409,410,411,168],{},"Here's a link to my code ",[20,412,415],{"href":413,"rel":414},"https:\u002F\u002Fgithub.com\u002Fdvalinotti\u002Fcomponents-with-closure",[24],"repo on GitHub",[16,417,418,419,168],{},"If you'd like to mess around with this yourself, here is a link to the ",[20,420,423],{"href":421,"rel":422},"https:\u002F\u002Fcodesandbox.io\u002Fs\u002Ffunny-cannon-o0p75?file=\u002Fsrc\u002Findex.js",[24],"project on CodeSandbox",[16,425,426],{},"Thanks for reading! 😊",{"title":428,"searchDepth":429,"depth":429,"links":430},"",2,[431,432,433,434,435,436,437],{"id":13,"depth":429,"text":14},{"id":54,"depth":429,"text":55},{"id":113,"depth":429,"text":114},{"id":193,"depth":429,"text":194},{"id":253,"depth":429,"text":254},{"id":350,"depth":429,"text":351},{"id":367,"depth":429,"text":368},"2021-06-03","md","closure-post-thumbnail.jpg","javascript,js,vanilla,vanilla javascript,components,function,var,const,let,closure,closures,scope,variable scope,tutorial",{},true,"\u002Fblog\u002Femulating-component-behavior-with-javascript-closures",{"title":6,"description":428},"blog\u002Femulating-component-behavior-with-javascript-closures","Read along as I create a poor man's Virtual DOM API.",[449,450],"javascript","tutorials","HLmsIjPXOiSLHRGuHnWmConT1PE6fX9u6OkUod3fJcI",1776925021361]