{"id":40,"date":"2023-08-21T03:41:00","date_gmt":"2023-08-21T03:41:00","guid":{"rendered":"http:\/\/fem.flywheelsites.com\/?p=40"},"modified":"2024-07-30T08:15:10","modified_gmt":"2024-07-30T13:15:10","slug":"vanilla-javascript-reactivity","status":"publish","type":"post","link":"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-reactivity\/","title":{"rendered":"Patterns for Reactivity with Modern Vanilla JavaScript"},"content":{"rendered":"\n<p>\u201cReactivity\u201d is how systems react to changes in data. There are many types of reactivity, but for this article, reactivity is&nbsp;<strong>when data changes, you do things<\/strong>.<\/p>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-todomvc\/\">Writing a TodoMVC App with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-reactivity\/\">Patterns for Reactivity with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/patterns-for-memory-efficient-dom-manipulation\/\">Patterns for Memory Efficient DOM Manipulation with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"reactivity-patterns-are-core-to-web-development\">Reactivity Patterns are Core to Web Development<\/h2>\n\n\n\n<p>We handle a lot with JavaScript in websites and web apps since the browser is an entirely asynchronous environment. We must respond to user inputs, communicate with servers, log, perform, etc. All these tasks involve updates to the UI, Ajax requests, browser URLs, and navigation changes, making cascading data changes a core aspect of web development.<\/p>\n\n\n\n<p>As an industry, we associate reactivity with frameworks, but you can learn a lot by implementing reactivity in pure JavaScript. We can mix and match these patterns to wire behavior to data changes.<\/p>\n\n\n\n<p>Learning core patterns with pure JavaScript will lead to less code and better performance in your web apps, no matter what tool or framework you use.<\/p>\n\n\n\n<p>I love learning patterns because they apply to any language and system. Patterns can be combined to solve your app\u2019s exact requirements, often leading to more performant and maintainable code.<\/p>\n\n\n\n<p>Hopefully, you\u2019ll learn new patterns to add to your toolbox, no matter what frameworks and libraries you use!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"pubsub-pattern-publish-subscriber\">PubSub Pattern (Publish Subscriber)<\/h2>\n\n\n\n<p>PubSub is one of the most foundational patterns for reactivity. Firing an event out with&nbsp;<code>publish()<\/code>&nbsp;allows anyone to listen to that event&nbsp;<code>subscribe()<\/code>&nbsp;and do whatever they want in a decoupled from whatever fires that event.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> pubSub = {\n  <span class=\"hljs-attr\">events<\/span>: {},\n  subscribe(event, callback) {\n    <span class=\"hljs-keyword\">if<\/span> (!<span class=\"hljs-keyword\">this<\/span>.events&#91;event]) <span class=\"hljs-keyword\">this<\/span>.events&#91;event] = &#91;];\n    <span class=\"hljs-keyword\">this<\/span>.events&#91;event].push(callback);\n  },\n  publish(event, data) {\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">this<\/span>.events&#91;event]) <span class=\"hljs-keyword\">this<\/span>.events&#91;event].forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">callback<\/span> =&gt;<\/span> callback(data));\n  }\n};\n\npubSub.subscribe(<span class=\"hljs-string\">'update'<\/span>, data =&gt; <span class=\"hljs-built_in\">console<\/span>.log(data));\npubSub.publish(<span class=\"hljs-string\">'update'<\/span>, <span class=\"hljs-string\">'Some update'<\/span>); <span class=\"hljs-comment\">\/\/ Some update<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Note the publisher has&nbsp;<em>no idea<\/em>&nbsp;of what is listening to it, so there is no way to unsubscribe or clean up after itself with this simple implementation.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"custom-events-native-browser-api-for-pubsub\">Custom Events: Native Browser API for PubSub<\/h3>\n\n\n\n<p>The browser has a JavaScript API for firing and subscribing to custom events. It allows you to send data along with the custom events using&nbsp;<code>dispatchEvent<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> pizzaEvent = <span class=\"hljs-keyword\">new<\/span> CustomEvent(<span class=\"hljs-string\">\"pizzaDelivery\"<\/span>, {\n  <span class=\"hljs-attr\">detail<\/span>: {\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">\"supreme\"<\/span>,\n  },\n});\n\n<span class=\"hljs-built_in\">window<\/span>.addEventListener(<span class=\"hljs-string\">\"pizzaDelivery\"<\/span>, (e) =&gt; <span class=\"hljs-built_in\">console<\/span>.log(e.detail.name));\n<span class=\"hljs-built_in\">window<\/span>.dispatchEvent(pizzaEvent);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>You can scope these custom events to any DOM node. In the code example, we use the global&nbsp;<code>window<\/code>&nbsp;object, also known as a global event bus, so anything in our app can listen and do something with the event data.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">div<\/span> <span class=\"hljs-attr\">id<\/span>=<span class=\"hljs-string\">\"pizza-store\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">div<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> pizzaEvent = <span class=\"hljs-keyword\">new<\/span> CustomEvent(<span class=\"hljs-string\">\"pizzaDelivery\"<\/span>, {\n  <span class=\"hljs-attr\">detail<\/span>: {\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">\"supreme\"<\/span>,\n  },\n});\n\n<span class=\"hljs-keyword\">const<\/span> pizzaStore = <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'#pizza-store'<\/span>);\npizzaStore.addEventListener(<span class=\"hljs-string\">\"pizzaDelivery\"<\/span>, (e) =&gt; <span class=\"hljs-built_in\">console<\/span>.log(e.detail.name));\npizzaStore.dispatchEvent(pizzaEvent);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h3 class=\"wp-block-heading\" id=\"class-instance-custom-events-subclassing-eventtarget\">Class Instance Custom Events: Subclassing EventTarget<\/h3>\n\n\n\n<p>We can subclass EventTarget to send out events on a class instance for our app to bind to:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">PizzaStore<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">EventTarget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>() {\n    <span class=\"hljs-keyword\">super<\/span>();\n  }\n  addPizza(flavor) {\n    <span class=\"hljs-comment\">\/\/ fire event directly on the class<\/span>\n    <span class=\"hljs-keyword\">this<\/span>.dispatchEvent(<span class=\"hljs-keyword\">new<\/span> CustomEvent(<span class=\"hljs-string\">\"pizzaAdded\"<\/span>, {\n      <span class=\"hljs-attr\">detail<\/span>: {\n        <span class=\"hljs-attr\">pizza<\/span>: flavor,\n      },\n    }));\n  }\n}\n\n<span class=\"hljs-keyword\">const<\/span> Pizzas = <span class=\"hljs-keyword\">new<\/span> PizzaStore();\nPizzas.addEventListener(<span class=\"hljs-string\">\"pizzaAdded\"<\/span>, (e) =&gt; <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Added Pizza:'<\/span>, e.detail.pizza));\nPizzas.addPizza(<span class=\"hljs-string\">\"supreme\"<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The cool thing about this is your events aren\u2019t firing globally on the window. You can fire an event directly on a class; anything in your app can wire up event listeners directly to that class.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"observer-pattern\">Observer Pattern<\/h2>\n\n\n\n<p>The observer pattern has the same basic premise as the PubSub pattern. It allows you to have behavior \u201csubscribed\u201d to a Subject. And when the Subject fires the&nbsp;<code>notify<\/code>&nbsp;method, it notifies everything subscribed.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Subject<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>() {\n    <span class=\"hljs-keyword\">this<\/span>.observers = &#91;];\n  }\n  addObserver(observer) {\n    <span class=\"hljs-keyword\">this<\/span>.observers.push(observer);\n  }\n  removeObserver(observer) {\n    <span class=\"hljs-keyword\">const<\/span> index = <span class=\"hljs-keyword\">this<\/span>.observers.indexOf(observer);\n    <span class=\"hljs-keyword\">if<\/span> (index &gt; <span class=\"hljs-number\">-1<\/span>) {\n      <span class=\"hljs-keyword\">this<\/span>.observers.splice(index, <span class=\"hljs-number\">1<\/span>);\n    }\n  }\n  notify(data) {\n    <span class=\"hljs-keyword\">this<\/span>.observers.forEach(<span class=\"hljs-function\"><span class=\"hljs-params\">observer<\/span> =&gt;<\/span> observer.update(data));\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Observer<\/span> <\/span>{\n  update(data) {\n    <span class=\"hljs-built_in\">console<\/span>.log(data);\n  }\n}\n\n<span class=\"hljs-keyword\">const<\/span> subject = <span class=\"hljs-keyword\">new<\/span> Subject();\n<span class=\"hljs-keyword\">const<\/span> observer = <span class=\"hljs-keyword\">new<\/span> Observer();\n\nsubject.addObserver(observer);\nsubject.notify(<span class=\"hljs-string\">'Everyone gets pizzas!'<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The main difference between this and PubSub is that the Subject knows about its observers and can remove them. They aren\u2019t&nbsp;<em>completely<\/em>&nbsp;decoupled like in PubSub.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"reactive-object-properties-with-proxies\">Reactive Object Properties with Proxies<\/h2>\n\n\n\n<p>Proxies in JavaScript can be the foundation for performing reactivity after setting or getting properties on an object.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> handler = {\n  <span class=\"hljs-attr\">get<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">target, property<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Getting property <span class=\"hljs-subst\">${property}<\/span>`<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> target&#91;property];\n  },\n  <span class=\"hljs-attr\">set<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">target, property, value<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Setting property <span class=\"hljs-subst\">${property}<\/span> to <span class=\"hljs-subst\">${value}<\/span>`<\/span>);\n    target&#91;property] = value;\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-literal\">true<\/span>; <span class=\"hljs-comment\">\/\/ indicates that the setting has been done successfully<\/span>\n  }\n};\n\n<span class=\"hljs-keyword\">const<\/span> pizza = { <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Margherita'<\/span>, <span class=\"hljs-attr\">toppings<\/span>: &#91;<span class=\"hljs-string\">'tomato sauce'<\/span>, <span class=\"hljs-string\">'mozzarella'<\/span>] };\n<span class=\"hljs-keyword\">const<\/span> proxiedPizza = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Proxy<\/span>(pizza, handler);\n\n<span class=\"hljs-built_in\">console<\/span>.log(proxiedPizza.name); <span class=\"hljs-comment\">\/\/ Outputs \"Getting property name\" and \"Margherita\"<\/span>\nproxiedPizza.name = <span class=\"hljs-string\">'Pepperoni'<\/span>; <span class=\"hljs-comment\">\/\/ Outputs \"Setting property name to Pepperoni\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>When you access or modify a property on the&nbsp;<code>proxiedPizza<\/code>, it logs a message to the console. But you could imagine wiring any functionality to property access on an object.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-individual-properties-objectdefineproperty\">Reactive Individual Properties: Object.defineProperty<\/h3>\n\n\n\n<p>You can do an identical thing for a specific property using&nbsp;<code>Object.defineProperty<\/code>. You can define getters and setters for properties and run code when a property is accessed or modified.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> pizza = {\n  <span class=\"hljs-attr\">_name<\/span>: <span class=\"hljs-string\">'Margherita'<\/span>, <span class=\"hljs-comment\">\/\/ Internal property<\/span>\n};\n\n<span class=\"hljs-built_in\">Object<\/span>.defineProperty(pizza, <span class=\"hljs-string\">'name'<\/span>, {\n  <span class=\"hljs-attr\">get<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Getting property name`<\/span>);\n    <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-keyword\">this<\/span>._name;\n  },\n  <span class=\"hljs-attr\">set<\/span>: <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">value<\/span>) <\/span>{\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Setting property name to <span class=\"hljs-subst\">${value}<\/span>`<\/span>);\n    <span class=\"hljs-keyword\">this<\/span>._name = value;\n  }\n});\n\n<span class=\"hljs-comment\">\/\/ Example usage:<\/span>\n<span class=\"hljs-built_in\">console<\/span>.log(pizza.name); <span class=\"hljs-comment\">\/\/ Outputs \"Getting property name\" and \"Margherita\"<\/span>\npizza.name = <span class=\"hljs-string\">'Pepperoni'<\/span>; <span class=\"hljs-comment\">\/\/ Outputs \"Setting property name to Pepperoni\"<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here, we\u2019re using&nbsp;<code>Object.defineProperty<\/code>&nbsp;to define a getter and setter for the name property of the pizza object. The actual value is stored in a private&nbsp;<code>_name<\/code>&nbsp;property, and the getter and setter provide access to that value while logging messages to the console.<\/p>\n\n\n\n<p><code>Object.defineProperty<\/code>&nbsp;is more verbose than using a&nbsp;<code>Proxy<\/code>, especially if you want to apply the same behavior to many properties. But it\u2019s a powerful and flexible way to define custom behavior for individual properties.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"asynchronous-reactive-data-with-promises\">Asynchronous Reactive Data with Promises<\/h3>\n\n\n\n<p>Let\u2019s make using the observers asynchronous! This way we can update the data and have multiple observers run asynchronously.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AsyncData<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>(initialData) {\n    <span class=\"hljs-keyword\">this<\/span>.data = initialData;\n    <span class=\"hljs-keyword\">this<\/span>.subscribers = &#91;];\n  }\n\n  <span class=\"hljs-comment\">\/\/ Subscribe to changes in the data<\/span>\n  subscribe(callback) {\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> callback !== <span class=\"hljs-string\">'function'<\/span>) {\n      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Callback must be a function'<\/span>);\n    }\n    <span class=\"hljs-keyword\">this<\/span>.subscribers.push(callback);\n  }\n\n  <span class=\"hljs-comment\">\/\/ Update the data and wait for all updates to complete<\/span>\n  <span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-keyword\">set<\/span>(key, value) {\n    <span class=\"hljs-keyword\">this<\/span>.data&#91;key] = value;\n\n    <span class=\"hljs-comment\">\/\/ Call the subscribed function and wait for it to resolve<\/span>\n    <span class=\"hljs-keyword\">const<\/span> updates = <span class=\"hljs-keyword\">this<\/span>.subscribers.map(<span class=\"hljs-keyword\">async<\/span> (callback) =&gt; {\n      <span class=\"hljs-keyword\">await<\/span> callback(key, value);\n    });\n\n    <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-built_in\">Promise<\/span>.allSettled(updates);\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here\u2019s a class that wraps a data object and triggers an update when the data changes.<\/p>\n\n\n\n<h4 class=\"wp-block-heading\" id=\"awaiting-our-async-observers\">Awaiting Our Async Observers<\/h4>\n\n\n\n<p>Let\u2019s say we want to wait until all subscriptions to our asynchronous reactive data are processed:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> data = <span class=\"hljs-keyword\">new<\/span> AsyncData({ <span class=\"hljs-attr\">pizza<\/span>: <span class=\"hljs-string\">'Pepperoni'<\/span> });\n\ndata.subscribe(<span class=\"hljs-keyword\">async<\/span> (key, value) =&gt; {\n  <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\"><span class=\"hljs-params\">resolve<\/span> =&gt;<\/span> setTimeout(resolve, <span class=\"hljs-number\">500<\/span>));\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Updated UI for <span class=\"hljs-subst\">${key}<\/span>: <span class=\"hljs-subst\">${value}<\/span>`<\/span>);\n});\n\ndata.subscribe(<span class=\"hljs-keyword\">async<\/span> (key, value) =&gt; {\n  <span class=\"hljs-keyword\">await<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Promise<\/span>(<span class=\"hljs-function\"><span class=\"hljs-params\">resolve<\/span> =&gt;<\/span> setTimeout(resolve, <span class=\"hljs-number\">1000<\/span>));\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Logged change for <span class=\"hljs-subst\">${key}<\/span>: <span class=\"hljs-subst\">${value}<\/span>`<\/span>);\n});\n\n<span class=\"hljs-comment\">\/\/ function to update data and wait for all updates to complete<\/span>\n<span class=\"hljs-keyword\">async<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">updateData<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-keyword\">await<\/span> data.set(<span class=\"hljs-string\">'pizza'<\/span>, <span class=\"hljs-string\">'Supreme'<\/span>); <span class=\"hljs-comment\">\/\/ This will call the subscribed functions and wait for their promises to resolve<\/span>\n  <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'All updates complete.'<\/span>);\n}\n\nupdateData();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Our&nbsp;<code>updateData<\/code>&nbsp;function is now async, so we can await all the subscribed functions to resolve before continuing our program. This pattern allows juggling asynchronous reactivity a bit simpler.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"reactive-systems\">Reactive Systems<\/h2>\n\n\n\n<p>Many more complex reactive systems are at the foundations of popular libraries and frameworks: hooks in React, Signals in Solid, Observables in Rx.js, and more. They usually have the same basic premise of when data changes, re-render the components or associated DOM fragments.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"observables-pattern-of-rxjs\">Observables (Pattern of Rx.js)<\/h3>\n\n\n\n<p>Observables and Observer Pattern are not the same despite being nearly the same word, lol.<\/p>\n\n\n\n<p>Observables allow you to define a way to produce a sequence of values over time. Here is a simple Observable primitive that provides a way to emit a sequence of values to subscribers, allowing them to react as those values are produced.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">Observable<\/span> <\/span>{\n  <span class=\"hljs-keyword\">constructor<\/span>(producer) {\n    <span class=\"hljs-keyword\">this<\/span>.producer = producer;\n  }\n\n  <span class=\"hljs-comment\">\/\/ Method to allow a subscriber to subscribe to the observable<\/span>\n  subscribe(observer) {\n    <span class=\"hljs-comment\">\/\/ Ensure the observer has the necessary functions<\/span>\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> observer !== <span class=\"hljs-string\">'object'<\/span> || observer === <span class=\"hljs-literal\">null<\/span>) {\n      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Observer must be an object with next, error, and complete methods'<\/span>);\n    }\n\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> observer.next !== <span class=\"hljs-string\">'function'<\/span>) {\n      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Observer must have a next method'<\/span>);\n    }\n\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> observer.error !== <span class=\"hljs-string\">'function'<\/span>) {\n      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Observer must have an error method'<\/span>);\n    }\n\n    <span class=\"hljs-keyword\">if<\/span> (<span class=\"hljs-keyword\">typeof<\/span> observer.complete !== <span class=\"hljs-string\">'function'<\/span>) {\n      <span class=\"hljs-keyword\">throw<\/span> <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Error<\/span>(<span class=\"hljs-string\">'Observer must have a complete method'<\/span>);\n    }\n\n    <span class=\"hljs-keyword\">const<\/span> unsubscribe = <span class=\"hljs-keyword\">this<\/span>.producer(observer);\n\n    <span class=\"hljs-comment\">\/\/ Return an object with an unsubscribe method<\/span>\n    <span class=\"hljs-keyword\">return<\/span> {\n      <span class=\"hljs-attr\">unsubscribe<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        <span class=\"hljs-keyword\">if<\/span> (unsubscribe &amp;&amp; <span class=\"hljs-keyword\">typeof<\/span> unsubscribe === <span class=\"hljs-string\">'function'<\/span>) {\n          unsubscribe();\n        }\n      },\n    };\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here\u2019s how you would use them:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ Create a new observable that emits three values and then completes<\/span>\n<span class=\"hljs-keyword\">const<\/span> observable = <span class=\"hljs-keyword\">new<\/span> Observable(<span class=\"hljs-function\"><span class=\"hljs-params\">observer<\/span> =&gt;<\/span> {\n  observer.next(<span class=\"hljs-number\">1<\/span>);\n  observer.next(<span class=\"hljs-number\">2<\/span>);\n  observer.next(<span class=\"hljs-number\">3<\/span>);\n  observer.complete();\n\n  <span class=\"hljs-comment\">\/\/ Optional: Return a function to handle any cleanup if the observer unsubscribes<\/span>\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Observer unsubscribed'<\/span>);\n  };\n});\n\n<span class=\"hljs-comment\">\/\/ Define an observer with next, error, and complete methods<\/span>\n<span class=\"hljs-keyword\">const<\/span> observer = {\n  <span class=\"hljs-attr\">next<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">value<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Received value:'<\/span>, value),\n  <span class=\"hljs-attr\">error<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">err<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Error:'<\/span>, err),\n  <span class=\"hljs-attr\">complete<\/span>: <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'Completed'<\/span>),\n};\n\n<span class=\"hljs-comment\">\/\/ Subscribe to the observable<\/span>\n<span class=\"hljs-keyword\">const<\/span> subscription = observable.subscribe(observer);\n\n<span class=\"hljs-comment\">\/\/ Optionally, you can later unsubscribe to stop receiving values<\/span>\nsubscription.unsubscribe();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>The critical component of an Observable is the&nbsp;<code>next()<\/code>&nbsp;method, which sends data to the observers. A&nbsp;<code>complete()<\/code>&nbsp;method for when the Observable stream closes. And an&nbsp;<code>error()<\/code>&nbsp;method when something goes wrong. Also, there has to be a way to&nbsp;<code>subscribe()<\/code>&nbsp;to listen for changes and&nbsp;<code>unsubscribe()<\/code>&nbsp;to stop receiving data from the stream.<\/p>\n\n\n\n<p>The most popular libraries that use this pattern are&nbsp;<a href=\"https:\/\/rxjs.dev\/\">Rx.js<\/a>&nbsp;and&nbsp;<a href=\"https:\/\/mobx.js.org\/\">MobX<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"signals-pattern-of-solidjs\">\u201cSignals\u201d (Pattern of SolidJS)<\/h3>\n\n\n\n<p>Hat tip Ryan Carniato\u2019s&nbsp;<a href=\"https:\/\/frontendmasters.com\/courses\/reactivity-solidjs\/?utm_source=blog&amp;utm_medium=website&amp;utm_campaign=reactivity\">Reactivity with SolidJS course<\/a>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> context = &#91;];\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createSignal<\/span>(<span class=\"hljs-params\">value<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> subscriptions = <span class=\"hljs-keyword\">new<\/span> <span class=\"hljs-built_in\">Set<\/span>();\n\n    <span class=\"hljs-keyword\">const<\/span> read = <span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n        <span class=\"hljs-keyword\">const<\/span> observer = context&#91;context.length - <span class=\"hljs-number\">1<\/span>]\n        <span class=\"hljs-keyword\">if<\/span> (observer) subscriptions.add(observer);\n        <span class=\"hljs-keyword\">return<\/span> value;\n    }\n    <span class=\"hljs-keyword\">const<\/span> write = <span class=\"hljs-function\">(<span class=\"hljs-params\">newValue<\/span>) =&gt;<\/span> {\n        value = newValue;\n        <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> observer <span class=\"hljs-keyword\">of<\/span> subscriptions) {\n            observer.execute()\n        }\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> &#91;read, write];\n}\n\n<span class=\"hljs-keyword\">export<\/span> <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">createEffect<\/span>(<span class=\"hljs-params\">fn<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">const<\/span> effect = {\n        execute() {\n            context.push(effect);\n            fn();\n            context.pop();\n        }\n    }\n\n    effect.execute();\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Using the reactive system:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> { createSignal, createEffect } <span class=\"hljs-keyword\">from<\/span> <span class=\"hljs-string\">\".\/reactive\"<\/span>;\n\n<span class=\"hljs-keyword\">const<\/span> &#91;count, setCount] = createSignal(<span class=\"hljs-number\">0<\/span>);\n\ncreateEffect(<span class=\"hljs-function\"><span class=\"hljs-params\">()<\/span> =&gt;<\/span> {\n  <span class=\"hljs-built_in\">console<\/span>.log(count());\n}); <span class=\"hljs-comment\">\/\/ 0<\/span>\n\nsetCount(<span class=\"hljs-number\">10<\/span>); <span class=\"hljs-comment\">\/\/ 10<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here\u2019s the complete code for his&nbsp;<a href=\"https:\/\/gist.github.com\/1Marc\/09e739caa6a82cc176ab4c2abd691814\">vanilla reactivity system<\/a>&nbsp;with a code sample that Ryan writes in his course.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"observable-ish-values-frontend-masters\">\u201cObservable-ish\u201d Values (Frontend Masters)<\/h3>\n\n\n\n<p>Our Frontend Masters video player has many configurations that could change anytime to modify video playback. Kai on our team created&nbsp;<a href=\"https:\/\/github.com\/FrontendMasters\/observablish-values\">\u201cObservable-ish\u201d Values<\/a>&nbsp;(many years ago now, but we just published it for this article\u2019s sake), which is another take on a reactive system in vanilla JavaScript.<\/p>\n\n\n\n<p>It\u2019s less than 100 lines of code and has stood the test of time! For 7+ years, this tiny bit of code has underpinned delivering millions of hours of video. It\u2019s a mix of PubSub with the ability to have computed values by adding the results of multiple publishers together.<\/p>\n\n\n\n<p>Here\u2019s how you use the \u201cObservable-ish\u201d values. Publish changes to subscriber functions when values change:<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> fn = <span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">current, previous<\/span>) <\/span>{}\n\n<span class=\"hljs-keyword\">const<\/span> obsValue = ov(<span class=\"hljs-string\">'initial'<\/span>);\nobsValue.subscribe(fn);      <span class=\"hljs-comment\">\/\/ subscribe to changes<\/span>\nobsValue();                  <span class=\"hljs-comment\">\/\/ 'initial'<\/span>\nobsValue(<span class=\"hljs-string\">'initial'<\/span>);         <span class=\"hljs-comment\">\/\/ identical value, no change<\/span>\nobsValue(<span class=\"hljs-string\">'new'<\/span>);             <span class=\"hljs-comment\">\/\/ fn('new', 'initial')<\/span>\nobsValue.value = <span class=\"hljs-string\">'silent'<\/span>;   <span class=\"hljs-comment\">\/\/ silent update<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Modifying arrays and objects will not publish, but replacing them will.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> obsArray = ov(&#91;<span class=\"hljs-number\">1<\/span>, <span class=\"hljs-number\">2<\/span>, <span class=\"hljs-number\">3<\/span>]);\nobsArray.subscribe(fn);\nobsArray().push(<span class=\"hljs-number\">4<\/span>);          <span class=\"hljs-comment\">\/\/ silent update<\/span>\nobsArray.publish();          <span class=\"hljs-comment\">\/\/ fn(&#91;1, 2, 3, 4]);<\/span>\nobsArray(&#91;<span class=\"hljs-number\">4<\/span>, <span class=\"hljs-number\">5<\/span>]);            <span class=\"hljs-comment\">\/\/ fn(&#91;4, 5], &#91;1, 2, 3]);<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Passing a function caches the result as the value. Any extra arguments will be passed to the function. Any observables called within the function will be subscribed to, and updates to those observables will recompute the value.<\/p>\n\n\n\n<p>Child observables must be called; mere references are ignored. If the function returns a Promise, the value is assigned async after resolution.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> a = ov(<span class=\"hljs-number\">1<\/span>);\n<span class=\"hljs-keyword\">const<\/span> b = ov(<span class=\"hljs-number\">2<\/span>);\n<span class=\"hljs-keyword\">const<\/span> computed = ov(<span class=\"hljs-function\"><span class=\"hljs-params\">arg<\/span> =&gt;<\/span> { a() + b() + arg }, <span class=\"hljs-number\">3<\/span>);\ncomputed.subscribe(fn);\ncomputed();                  <span class=\"hljs-comment\">\/\/ fn(6)<\/span>\na(<span class=\"hljs-number\">2<\/span>);                        <span class=\"hljs-comment\">\/\/ fn(7, 6)<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"reactive-rendering-of-ui\">Reactive Rendering of UI<\/h2>\n\n\n\n<p>Here are some patterns for writing and reading from the DOM and CSS.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"render-data-to-html-string-literals\">Render Data to HTML String Literals<\/h3>\n\n\n\n<p>Here\u2019s a simple example of rendering some pizza UI based on data.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PizzaRecipe<\/span>(<span class=\"hljs-params\">pizza<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">`&lt;div class=\"pizza-recipe\"&gt;\n    &lt;h1&gt;<span class=\"hljs-subst\">${pizza.name}<\/span>&lt;\/h1&gt;\n    &lt;h3&gt;Toppings: <span class=\"hljs-subst\">${pizza.toppings.join(<span class=\"hljs-string\">', '<\/span>)}<\/span>&lt;\/h3&gt;\n    &lt;p&gt;<span class=\"hljs-subst\">${pizza.description}<\/span>&lt;\/p&gt;\n  &lt;\/div&gt;`<\/span>;\n}\n\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">PizzaRecipeList<\/span>(<span class=\"hljs-params\">pizzas<\/span>) <\/span>{\n  <span class=\"hljs-keyword\">return<\/span> <span class=\"hljs-string\">`&lt;div class=\"pizza-recipe-list\"&gt;\n    <span class=\"hljs-subst\">${pizzas.map(PizzaRecipe).join(<span class=\"hljs-string\">''<\/span>)}<\/span>\n  &lt;\/div&gt;`<\/span>;\n}\n\n<span class=\"hljs-keyword\">var<\/span> allPizzas = &#91;\n  {\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Margherita'<\/span>,\n    <span class=\"hljs-attr\">toppings<\/span>: &#91;<span class=\"hljs-string\">'tomato sauce'<\/span>, <span class=\"hljs-string\">'mozzarella'<\/span>],\n    <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">'A classic pizza with fresh ingredients.'<\/span>\n  },\n  {\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Pepperoni'<\/span>,\n    <span class=\"hljs-attr\">toppings<\/span>: &#91;<span class=\"hljs-string\">'tomato sauce'<\/span>, <span class=\"hljs-string\">'mozzarella'<\/span>, <span class=\"hljs-string\">'pepperoni'<\/span>],\n    <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">'A favorite among many, topped with delicious pepperoni.'<\/span>\n  },\n  {\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Veggie Supreme'<\/span>,\n    <span class=\"hljs-attr\">toppings<\/span>: &#91;<span class=\"hljs-string\">'tomato sauce'<\/span>, <span class=\"hljs-string\">'mozzarella'<\/span>, <span class=\"hljs-string\">'bell peppers'<\/span>, <span class=\"hljs-string\">'onions'<\/span>, <span class=\"hljs-string\">'mushrooms'<\/span>],\n    <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">'A delightful vegetable-packed pizza.'<\/span>\n  }\n];\n\n<span class=\"hljs-comment\">\/\/ Render the list of pizzas<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">renderPizzas<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'body'<\/span>).innerHTML = PizzaRecipeList(allPizzas);\n}\n\nrenderPizzas(); <span class=\"hljs-comment\">\/\/ Initial render<\/span>\n\n<span class=\"hljs-comment\">\/\/ Example of changing data and re-rendering<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">addPizza<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  allPizzas.push({\n    <span class=\"hljs-attr\">name<\/span>: <span class=\"hljs-string\">'Hawaiian'<\/span>,\n    <span class=\"hljs-attr\">toppings<\/span>: &#91;<span class=\"hljs-string\">'tomato sauce'<\/span>, <span class=\"hljs-string\">'mozzarella'<\/span>, <span class=\"hljs-string\">'ham'<\/span>, <span class=\"hljs-string\">'pineapple'<\/span>],\n    <span class=\"hljs-attr\">description<\/span>: <span class=\"hljs-string\">'A tropical twist with ham and pineapple.'<\/span>\n  });\n\n  renderPizzas(); <span class=\"hljs-comment\">\/\/ Re-render the updated list<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Call this function to add a new pizza and re-render the list<\/span>\naddPizza();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p><code>addPizza<\/code>&nbsp;demonstrates how to change the data by adding a new pizza recipe to the list and then re-rendering the list to reflect the changes.<\/p>\n\n\n\n<p>The main drawback of this approach is you blow away the entire DOM on every render. You can more intelligently update only the bits of DOM that change using a library like&nbsp;<a href=\"https:\/\/www.npmjs.com\/package\/lit-html\">lit-html<\/a>&nbsp;(<a href=\"https:\/\/lit.dev\/docs\/libraries\/standalone-templates\/\">lit-html usage guide<\/a>). We do this with several highly dynamic components on Frontend Masters, like our data grid component.<\/p>\n\n\n\n<p>See examples of other approaches in the&nbsp;<a href=\"https:\/\/github.com\/1Marc\/modern-todomvc-vanillajs\">Vanilla TodoMVC repo<\/a>&nbsp;and associated&nbsp;<a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-todomvc\/\">Vanilla TodoMVC article<\/a>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-dom-attributes-mutationobserver\">Reactive DOM Attributes: MutationObserver<\/h3>\n\n\n\n<p>One way to make DOM reactive is to add and remove attributes. We can listen to changes in attributes using the&nbsp;<code>MutationObserver<\/code>&nbsp;API.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> mutationCallback = <span class=\"hljs-function\">(<span class=\"hljs-params\">mutationsList<\/span>) =&gt;<\/span> {\n  <span class=\"hljs-keyword\">for<\/span> (<span class=\"hljs-keyword\">const<\/span> mutation <span class=\"hljs-keyword\">of<\/span> mutationsList) {\n    <span class=\"hljs-keyword\">if<\/span> (\n      mutation.type !== <span class=\"hljs-string\">\"attributes\"<\/span> ||\n      mutation.attributeName !== <span class=\"hljs-string\">\"pizza-type\"<\/span>\n    ) <span class=\"hljs-keyword\">return<\/span>;\n\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'old:'<\/span>, mutation.oldValue)\n    <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">'new:'<\/span>, mutation.target.getAttribute(<span class=\"hljs-string\">\"pizza-type\"<\/span>))\n  }\n}\n<span class=\"hljs-keyword\">const<\/span> observer = <span class=\"hljs-keyword\">new<\/span> MutationObserver(mutationCallback);\nobserver.observe(<span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'pizza-store'<\/span>), { <span class=\"hljs-attr\">attributes<\/span>: <span class=\"hljs-literal\">true<\/span> });\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Now we can update the pizza-type attribute from anywhere in our program, and the element itself can have behavior attached to updating that attribute!<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-attributes-in-web-components\">Reactive Attributes in Web Components<\/h3>\n\n\n\n<p>With Web Components, there is a native way to listen and react to attribute updates.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">PizzaStoreComponent<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">HTMLElement<\/span> <\/span>{\n  <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">get<\/span> observedAttributes() {\n    <span class=\"hljs-keyword\">return<\/span> &#91;<span class=\"hljs-string\">'pizza-type'<\/span>];\n  }\n\n  <span class=\"hljs-keyword\">constructor<\/span>() {\n    <span class=\"hljs-keyword\">super<\/span>();\n    <span class=\"hljs-keyword\">const<\/span> shadowRoot = <span class=\"hljs-keyword\">this<\/span>.attachShadow({ <span class=\"hljs-attr\">mode<\/span>: <span class=\"hljs-string\">'open'<\/span> });\n    shadowRoot.innerHTML = <span class=\"hljs-string\">`&lt;p&gt;<span class=\"hljs-subst\">${<span class=\"hljs-keyword\">this<\/span>.getAttribute(<span class=\"hljs-string\">'pizza-type'<\/span>) || <span class=\"hljs-string\">'Default Content'<\/span>}<\/span>&lt;\/p&gt;`<\/span>;\n  }\n\n  attributeChangedCallback(name, oldValue, newValue) {\n    <span class=\"hljs-keyword\">if<\/span> (name === <span class=\"hljs-string\">'my-attribute'<\/span>) {\n      <span class=\"hljs-keyword\">this<\/span>.shadowRoot.querySelector(<span class=\"hljs-string\">'div'<\/span>).textContent = newValue;\n      <span class=\"hljs-built_in\">console<\/span>.log(<span class=\"hljs-string\">`Attribute <span class=\"hljs-subst\">${name}<\/span> changed from <span class=\"hljs-subst\">${oldValue}<\/span> to <span class=\"hljs-subst\">${newValue}<\/span>`<\/span>);\n    }\n  }\n}\n\ncustomElements.define(<span class=\"hljs-string\">'pizza-store'<\/span>, PizzaStoreComponent);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"HTML, XML\" data-shcb-language-slug=\"xml\"><span><code class=\"hljs language-xml\"><span class=\"hljs-tag\">&lt;<span class=\"hljs-name\">pizza-store<\/span> <span class=\"hljs-attr\">pizza-type<\/span>=<span class=\"hljs-string\">\"Supreme\"<\/span>&gt;<\/span><span class=\"hljs-tag\">&lt;\/<span class=\"hljs-name\">pizza-store<\/span>&gt;<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">HTML, XML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">xml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-built_in\">document<\/span>.querySelector(<span class=\"hljs-string\">'pizza-store'<\/span>).setAttribute(<span class=\"hljs-string\">'pizza-type'<\/span>, <span class=\"hljs-string\">'BBQ Chicken!'<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is a bit simpler, but we have to use Web Components to use this API.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-scrolling-intersectionobserver\">Reactive Scrolling: IntersectionObserver<\/h3>\n\n\n\n<p>We can wire reactivity to DOM elements scrolling into view. I\u2019ve used this for slick animations on our marketing pages.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">var<\/span> pizzaStoreElement = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'pizza-store'<\/span>);\n\n<span class=\"hljs-keyword\">var<\/span> observer = <span class=\"hljs-keyword\">new<\/span> IntersectionObserver(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">entries, observer<\/span>) <\/span>{\n  entries.forEach(<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span>(<span class=\"hljs-params\">entry<\/span>) <\/span>{\n    <span class=\"hljs-keyword\">if<\/span> (entry.isIntersecting) {\n      entry.target.classList.add(<span class=\"hljs-string\">'animate-in'<\/span>);\n    } <span class=\"hljs-keyword\">else<\/span> {\n      entry.target.classList.remove(<span class=\"hljs-string\">'animate-in'<\/span>);\n    }\n  });\n});\n\nobserver.observe(pizzaStoreElement);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>Here\u2019s an example&nbsp;<a href=\"https:\/\/codepen.io\/1Marc\/pen\/wvEKOEr\">scrolling animation CodePen<\/a>&nbsp;in very few lines of code using&nbsp;<code>IntersectionObserver<\/code>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"animation--game-loop-requestanimationframe\">Animation &amp; Game Loop: requestAnimationFrame<\/h3>\n\n\n\n<p>When working with game development, Canvas, WebGL, or those wild marketing sites, animations often require writing to a buffer and then writing the results on a given loop when the rendering thread becomes available. We do this with&nbsp;<code>requestAnimationFrame<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">drawStuff<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  <span class=\"hljs-comment\">\/\/ This is where you'd do game or animation rendering logic<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ function to handle the animation<\/span>\n<span class=\"hljs-function\"><span class=\"hljs-keyword\">function<\/span> <span class=\"hljs-title\">animate<\/span>(<span class=\"hljs-params\"><\/span>) <\/span>{\n  drawStuff();\n  requestAnimationFrame(animate); <span class=\"hljs-comment\">\/\/ Continually calls animate when the next render frame is available<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Start the animation<\/span>\nanimate();\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>This is the method games and anything that involves real-time rendering use to render the scene when frames become available.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-animations-web-animations-api\">Reactive Animations: Web Animations API<\/h3>\n\n\n\n<p>You can also create reactive animations with the Web Animations API. Here we will animate an element\u2019s scale, position, and color using the animation API.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">const<\/span> el = <span class=\"hljs-built_in\">document<\/span>.getElementById(<span class=\"hljs-string\">'animatedElement'<\/span>);\n\n<span class=\"hljs-comment\">\/\/ Define the animation properties<\/span>\n<span class=\"hljs-keyword\">const<\/span> animation = el.animate(&#91;\n  <span class=\"hljs-comment\">\/\/ Keyframes<\/span>\n  { <span class=\"hljs-attr\">transform<\/span>: <span class=\"hljs-string\">'scale(1)'<\/span>, <span class=\"hljs-attr\">backgroundColor<\/span>: <span class=\"hljs-string\">'blue'<\/span>, <span class=\"hljs-attr\">left<\/span>: <span class=\"hljs-string\">'50px'<\/span>, <span class=\"hljs-attr\">top<\/span>: <span class=\"hljs-string\">'50px'<\/span> },\n  { <span class=\"hljs-attr\">transform<\/span>: <span class=\"hljs-string\">'scale(1.5)'<\/span>, <span class=\"hljs-attr\">backgroundColor<\/span>: <span class=\"hljs-string\">'red'<\/span>, <span class=\"hljs-attr\">left<\/span>: <span class=\"hljs-string\">'200px'<\/span>, <span class=\"hljs-attr\">top<\/span>: <span class=\"hljs-string\">'200px'<\/span> }\n], {\n  <span class=\"hljs-comment\">\/\/ Timing options<\/span>\n  <span class=\"hljs-attr\">duration<\/span>: <span class=\"hljs-number\">1000<\/span>,\n  <span class=\"hljs-attr\">fill<\/span>: <span class=\"hljs-string\">'forwards'<\/span>\n});\n\n<span class=\"hljs-comment\">\/\/ Set the animation's playback rate to 0 to pause it<\/span>\nanimation.playbackRate = <span class=\"hljs-number\">0<\/span>;\n\n<span class=\"hljs-comment\">\/\/ Add a click event listener to the element<\/span>\nel.addEventListener(<span class=\"hljs-string\">'click'<\/span>, () =&gt; {\n  <span class=\"hljs-comment\">\/\/ If the animation is paused, play it<\/span>\n  <span class=\"hljs-keyword\">if<\/span> (animation.playbackRate === <span class=\"hljs-number\">0<\/span>) {\n    animation.playbackRate = <span class=\"hljs-number\">1<\/span>;\n  } <span class=\"hljs-keyword\">else<\/span> {\n    <span class=\"hljs-comment\">\/\/ If the animation is playing, reverse it<\/span>\n    animation.reverse();\n  }\n});\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>What\u2019s reactive about this is that the animation can play relative to where it is located when an interaction occurs (in this case, reversing its direction). Standard CSS animations and transitions aren\u2019t relative to their current position.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"reactive-css-custom-properties-and-calc\">Reactive CSS: Custom Properties and&nbsp;<code>calc<\/code><\/h3>\n\n\n\n<p>Lastly, we can write CSS that\u2019s reactive by combining custom properties and&nbsp;<code>calc<\/code>.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">barElement.style.setProperty(<span class=\"hljs-string\">'--percentage'<\/span>, newPercentage);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>In JavaScript, you can set a custom property value.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"CSS\" data-shcb-language-slug=\"css\"><span><code class=\"hljs language-css\"><span class=\"hljs-selector-class\">.bar<\/span> {\n  <span class=\"hljs-attribute\">width<\/span>: <span class=\"hljs-built_in\">calc<\/span>(<span class=\"hljs-number\">100%<\/span> \/ <span class=\"hljs-number\">4<\/span> - <span class=\"hljs-number\">10px<\/span>);\n  <span class=\"hljs-attribute\">height<\/span>: <span class=\"hljs-built_in\">calc<\/span>(var(--percentage) * <span class=\"hljs-number\">1%<\/span>);\n  <span class=\"hljs-attribute\">background-color<\/span>: blue;\n  <span class=\"hljs-attribute\">margin-right<\/span>: <span class=\"hljs-number\">10px<\/span>;\n  <span class=\"hljs-attribute\">position<\/span>: relative;\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">CSS<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">css<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<p>And in the CSS, we can now do calculations based on that percentage. It\u2019s pretty cool that we can add calculations right into the CSS and let CSS do its job of styling without having to keep all that rendering logic in JavaScript.<\/p>\n\n\n\n<p>FYI: You can also read these properties if you want to create changes relative to the current value.<\/p>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\">getComputedStyle(barElement).getPropertyValue(<span class=\"hljs-string\">'--percentage'<\/span>);\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<h2 class=\"wp-block-heading\" id=\"the-many-ways-to-achieve-reactivity\">The Many Ways to Achieve Reactivity<\/h2>\n\n\n\n<p>It\u2019s incredible how many ways we can achieve reactivity using very little code in modern vanilla JavaScript. We can combine these patterns in any way we see fit for our apps to reactively render, log, animate, handle user events, and all the things that can happen in the browser. <\/p>\n\n\n\n<div class=\"wp-block-group learn-more\"><div class=\"wp-block-group__inner-container is-layout-constrained wp-block-group-is-layout-constrained\">\n<p>Next, check out the&nbsp;<a href=\"https:\/\/frontendmasters.com\/learn\/javascript\/?utm_source=blog&amp;utm_medium=website&amp;utm_campaign=reactivity\">JavaScript Learning Path<\/a>&nbsp;and learn JavaScript deeply from awesome instructors like Anjana Vakil, Will Sentance and Kyle Simpson! Or dive right into the most loved course on the platform,&nbsp;<a href=\"https:\/\/frontendmasters.com\/courses\/javascript-hard-parts-v2\/?utm_source=blog&amp;utm_medium=website&amp;utm_campaign=reactivity\">JavaScript: The Hard Parts<\/a>!<\/p>\n\n\n\n<p>~ Frontend Masters Team<\/p>\n<\/div><\/div>\n\n\n<div class=\"box article-series\">\n  <header>\n    <h3 class=\"article-series-header\">Article Series<\/h3>\n  <\/header>\n  <div class=\"box-content\">\n            <ol>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-todomvc\/\">Writing a TodoMVC App with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/vanilla-javascript-reactivity\/\">Patterns for Reactivity with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                      <li>\n              <a href=\"https:\/\/frontendmasters.com\/blog\/patterns-for-memory-efficient-dom-manipulation\/\">Patterns for Memory Efficient DOM Manipulation with Modern Vanilla JavaScript<\/a>\n            <\/li>\n                  <\/ol>\n        <\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>\u201cReactivity\u201d is how systems react to changes in data. There are many types of reactivity, but for this article, reactivity is\u00a0when data changes, you do things.<\/p>\n","protected":false},"author":2,"featured_media":43,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"sig_custom_text":"","sig_image_type":"featured-image","sig_custom_image":0,"sig_is_disabled":false,"inline_featured_image":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[1],"tags":[3,28,4],"class_list":["post-40","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-blog-post","tag-javascript","tag-reactivity","tag-vanilla"],"acf":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/frontendmasters.com\/blog\/wp-content\/uploads\/2023\/10\/reactivity-featured.png?fit=1000%2C500&ssl=1","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/40","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/users\/2"}],"replies":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/comments?post=40"}],"version-history":[{"count":8,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/40\/revisions"}],"predecessor-version":[{"id":3289,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/posts\/40\/revisions\/3289"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media\/43"}],"wp:attachment":[{"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/media?parent=40"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/categories?post=40"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/frontendmasters.com\/blog\/wp-json\/wp\/v2\/tags?post=40"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}