<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Pony Foo]]></title><description><![CDATA[Latest articles published on Pony Foo]]></description><link>https://ponyfoo.com</link><image><url>https://ponyfoo.com/img/banners/branded.b2b85b77.png</url><title>Pony Foo</title><link>https://ponyfoo.com</link></image><generator>ponyfoo/ponyfoo</generator><lastBuildDate>Thu, 06 Jun 2024 11:07:48 GMT</lastBuildDate><atom:link href="https://ponyfoo.com/articles/feed" rel="self" type="application/rss+xml"/><pubDate>Thu, 06 Jun 2024 11:07:48 GMT</pubDate><copyright><![CDATA[Nicolás Bevacqua, 2024]]></copyright><language><![CDATA[en]]></language><managingEditor><![CDATA[feed@ponyfoo.com (Nicolás Bevacqua)]]></managingEditor><webMaster><![CDATA[feed@ponyfoo.com (Nicolás Bevacqua)]]></webMaster><ttl>15</ttl><category><![CDATA[architecture]]></category><category><![CDATA[react]]></category><category><![CDATA[data]]></category><category><![CDATA[state]]></category><category><![CDATA[hooks]]></category><category><![CDATA[refs]]></category><category><![CDATA[autosave]]></category><category><![CDATA[patterns]]></category><category><![CDATA[jest]]></category><category><![CDATA[testing]]></category><category><![CDATA[git]]></category><category><![CDATA[tips]]></category><category><![CDATA[action-pattern]]></category><category><![CDATA[javascript]]></category><category><![CDATA[redux]]></category><category><![CDATA[graphql]]></category><category><![CDATA[apollo]]></category><category><![CDATA[performance]]></category><category><![CDATA[v8]]></category><category><![CDATA[internals]]></category><item><title><![CDATA[Bootstrapping a UI component library]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Building &#x2014; and adopting &#x2014; a component library in the context of a vibrant business is no easy feat. Here are a few things we&#x2019;ve learned.</p><p>At a small startup, the timing is never quite right to start a component library. Extremely quick iteration is not usually associated with putting together a standardized set of components and conventions to use across your app, as your components and conventions change all the time in the frenzy of finding product-market fit!</p> <p>Nevertheless, component libraries can be tremendously valuable in separating design concerns from feature development and business logic, eventually leading to faster-than-ever iteration. In this article we&#x2019;ll explore what went into our component library <em>&#x2014; we called it Ryu &#x2014;</em> and what we left out, the tradeoffs we made along the way, and the vision for its future. Let&#x2019;s go!</p><h2 id="what-does-a-component-library-consist-of">What does a component library consist of?</h2> <p>In essence, our component library consists of colors, sizes, fonts, line heights, and even media breakpoints and z-indices. When building a component library you don&#x2019;t generally come up with these values, but rather you take them from what Design has already fleshed out <em>&#x2014; probably in Figma.</em> The role of our component library is to mirror Figma as closely as possible, so that developers need not worry about getting the design aspect of their feature work, and can instead focus on getting business logic and its edge cases right. In this sense, the component library can and should be the bridge that melds together Design, Product, and Engineering, effectively acting as the source of truth for our design system is.</p> <p>To that end, we need an understanding across the organization that, while Design has the last word on how the experience we offer looks and feels, Engineering requires it be standardized into a core set of functionality like inputs, buttons, tooltips, menus, tables, drawers, and anything else the designs require. This distinction essentially buckets design work into two categories: new core components or modification to existing ones, and feature work that relies exclusively on core components and their many variations. Product&#x2019;s role in this collaboration is keeping both sides accountable and ensuring new designs are smoothly implemented so that feature work can leverage the latest. Aligning on this across the organization is key in ensuring we offer a consistent experience, where tweaks to components are leveraged across the product instead of making a change that only applies to one particular project or feature.</p> <p>This strict separation between larger feature work and the components that make up those features affords your team the opportunity to stop and think about how changing the design system fits with the rest of the design and the app. It also forces engineering to be more involved in the design process, offering its perspectives on UX and feasibility.</p> <p>So we have variables, tighter collaboration and feedback loops, and a strict separation of design concerns and feature work. An important point to make next is building <strong>a hard boundary</strong> between the main codebase and your component library.</p> <h2 id="isolation">Isolation</h2> <p>You might be tempted to argue in favor of keeping the component library in the main codebase <em>&#x2014; just put the components in their own directory but keep them in the main repo.</em> This is a good first step, but it&#x2019;s not good enough. Having a separate repository is beneficial in many ways:</p> <ul> <li>Feature work doesn&#x2019;t get to make drive-by &#x201C;small tweaks&#x201D; to the component library as a side effect</li> <li>Designers can interact with the component library in isolation without having to worry about business logic, in a codebase that&#x2019;s typically a lot easier to digest</li> <li>Satellite efforts like internal tools, an engineering blog, or documentation sites can easily leverage the component library if it&#x2019;s isolated</li> <li>The component library can&#x2019;t be tainted by the main application, and isn&#x2019;t constrained to it, meaning there&#x2019;s no intermingling of business logic and design concerns</li> <li>It&#x2019;s a forcing function to properly document your component interfaces, which self-reinforces as better interfaces for its consumers</li> <li>In a similar vein, you&#x2019;re also forced to think about the design system in the larger scheme of things. It&#x2019;s not just meant for your core application anymore, so this forces us to think about the essence of what a component is trying to achieve, and to create simpler, more general purpose interfaces</li> </ul> <p>Once you decide to isolate the component library and can leverage it across the company, it&#x2019;s important to draw lines. The component library interface is to be fiercely protected, lest it degrade as a result of trying to appease every potential use case. When designing its interfaces, always take a step back and think about the use cases. What purpose does this new setting serve? Is there a more general way of achieving the same functionality that doesn&#x2019;t back us into a corner? Do we even need to make a change here, or is there a solution that doesn&#x2019;t involve any changes to the component library? And of course, the cardinal sin of component design <em>&#x2014; would we end up with multiple ways of achieving the same outcome?</em></p> <p>Good component libraries are like a good wine, they need time to breathe. It&#x2019;s important to recognize that you <strong>don&#x2019;t</strong> need to support every use case. Think of the context your component library is born in. It is unlikely the component library will be the first project the company embarks on. By the time you start thinking about developing a component library, a vibrant product development team is churning away features and likely already has an existing set of components they&#x2019;ve outgrown. These components were iterated on heavily, the experience they provide is likely really good, and if you&#x2019;re thinking about starting a component library afresh, chances are their interfaces have become less than ideal due to a combination of small tweaks done as part of feature work and a proclivity to favor speed of iteration above nearly all else. This is not necessarily a bad thing, priorities change, and you now have a deep well of insight to draw from.</p> <p>So when bootstrapping a component library, we can start with the obvious. Tackle the aspects of existing components that have a clearly superior solution, and ignore others for a while. As the library builds out and time lapses, patterns emerge, become obvious, and then you can incorporate them. Punt on problems you don&#x2019;t have a good solution for. Rather than implementing a bad interface, do nothing. It&#x2019;s not like the component library will suffer from it: you&#x2019;re preserving the status quo, consumers can always go use the old components that support every use case under the sun. Your goal isn&#x2019;t to support everything yet, but to end up with a package that eventually can support almost every use case, and in order to do that you need a solid foundation that doesn&#x2019;t overextend itself.</p> <h2 id="consumption">Consumption</h2> <p>Let&#x2019;s say you expose your component library as an npm package (private or otherwise). What else should you have? Even when the library is internal to your company, it&#x2019;s always a good idea to treat the project as if it were open-source.</p> <p>Yes, this means maintaining a hand-crafted changelog, so that developers can keep appraised about what&#x2019;s happening in the UI library and how it evolved over time, and proper versioning, cutting releases the team can adopt at their own pace.</p> <figure><img alt="GitHub Releases" title="github_release.be6ff837.png" class src="https://images.ponyfoo.com/uploads/github_release.be6ff837-93b1fa1fd4d84089a775a1adb90c7dc5.png"><figcaption>GitHub Releases</figcaption></figure> <p>As with any open-source project, you should take great care of building an approachable interface. Extensively using TypeScript could also be a great way of nudging consumers towards addressing breaking changes.</p> <p>One of the ultimate goals of a component library is lowering the bar for UI contributions, so that <em>&#x2014; for example &#x2014;</em> folks working on the backend can contribute bits and pieces of UI work without having to know much about design or the patterns we use, since all of that is encapsulated in the library.</p> <p>It also enables for faster iteration by virtue of being unable to make tweaks to the components as you go, and instead forcing the organization to make changes to the component library separately (which lets us weigh those changes on their own merit). This helps streamline processes and funnels all design changes through a dedicated channel.</p> <p>Having clear documentation that covers all major use cases <em>(and then some more)</em> is crucial in getting adoption. Documentation is the opportunity to abstract developers away from Figma and focus them towards feature work. A lack of proper documentation can be catastrophic for adoption, given folks would not know how to use the library or what the preferred style or patterns are, resulting in them rightfully defaulting to whatever alternative existed before the component library came to life.</p> <figure><img alt="Documentation Site" title="docs_site.b6d931b8.png" class src="https://images.ponyfoo.com/uploads/docs_site.b6d931b8-aad6f871b40648aa9733265e994f43ca.png"><figcaption>Documentation Site</figcaption></figure> <p>Documentation is also an excellent way of talking with Design. Designers who can code make non-trivial contributions to the component library and tighten the link between Figma and code. Without a component library, designers will rarely feel adequate contributing style changes, fearing they might cause some undesirable side effect. The component library, being more isolated, makes contribution feel safer. The more examples you have for consumers, the better the experience a designer has contributing can be.</p> <p>There are well known tools for writing documentation sites, but when it comes to a component library, we argue that nothing beats using the component library itself as the building blocks for its own documentation site. Right off the bat this gives you a second consumer, with its own set of needs. It also forces you to write an application from scratch using the component library. At first it might look underwhelming, but you can use those rough edges to your advantage, sharpening the component library itself.</p> <h2 id="architecture">Architecture</h2> <p>To reiterate, you probably aren&#x2019;t going to build a component library in a vacuum. Ramp was 799 days old when we started building ours. We are building it for the long term, and it&#x2019;s equally important to build on the context we already have. To that end, we picked technologies we are already using in the main application, and which we&#x2019;re already comfortable with, such as React, TypeScript, Babel, <a href="https://styled-components.com/" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">styled-components</code></a>, and <a href="https://github.com/downshift-js/downshift" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">downshift</code></a>. By leaning on these technologies, we were able to iterate quickly.</p> <p>We already had a color palette, and standardized sizing units, breakpoints, and so on. When it comes to the library, we made the conscious choice to add a layer of indirection here. Instead of naming colors, for instance, we assigned semantic meaning to them. We assigned a color to signify <code class="md-code md-code-inline">destructive</code> outcomes, a color to signify <code class="md-code md-code-inline">constructive</code> outcomes, and so on. Having this layer of indirection decouples design from implementation, accomplishing two things. First, it lets us change implementation details <em>&#x2014; such as the exact hue we use for a &#x201C;destructive&#x201D; button &#x2014;</em> without changing the interface consumers interact with. Second, it means consumers don&#x2019;t have to worry about it. Third, semantic meaning and indirection are crucial to make theming implementations feel effortless. (Fine, three things).</p> <p>We can take it a bit further and have a &#x201C;destructive&#x201D; background which translates as a light red tint, or a &#x201C;destructive&#x201D; button that&#x2019;s bright red. Consumers don&#x2019;t care about any of this. All they care about is having the component library render the correct interface for whatever Design decreed to be the correct &#x201C;destructive&#x201D; aesthetic for any given component. We can apply this concept across the board when it comes to magic numbers. Instead of knowing magic numbers, consumers talk in terms of whether something is large or small, so it makes sense for <em>that</em> to be the interface.</p> <blockquote> <p>At this point you might argue: &#x201C;but what if consumers want to display something slightly differently&#x201D;, and that hits a big aspect of having a well-crafted component library: it constrains consumers&#x2019; choices, in such a way that there&#x2019;s very few ways of accomplishing exactly what they want <em>&#x2014; so that everyone&#x2019;s code looks the same &#x2014;</em> and so that there&#x2019;s as little customization going on as possible <em>&#x2014; so that everyone&#x2019;s feature looks and feels the same.</em></p> <p>That is to say: &#x201C;they can&#x2019;t, and that&#x2019;s a feature not a bug&#x201D;.</p> </blockquote> <p>We also already had many components that had been heavily iterated on, and this is a trickier but crucial decision point: do you port these over or take the opportunity <em>&#x2014; and spend the time &#x2014;</em> to rework them? Both options have some merit, let&#x2019;s explore.</p> <p>Porting over existing components has the advantage of being quick. Once migrated to the new repo, you can easily port over the usage of existing components to the new ones. That said, existing components may be intertwined with business logic or deeply ingrained in the core codebase, complicating the extraction. At the same time, at the end of this exercise, you might not have a lot to show for. Sure, components are now isolated, but they still carry the technical debt that accumulated over years of quick iteration, and it&#x2019;ll be just as hard to clean up their interface now as it was before the migration. At least you&#x2019;ve stopped the bleeding: contributors are no longer able to make changes to core components as part of their normal workflow, they still should be able to change the core components, but they&#x2019;re forced to go through a dedicated process: contribute to a different repository, triggering a discussion with the maintainers of core components in Engineering, Design, and so on.</p> <p>Reworking components as you port them into the component library has its own set of considerations. For one, it&#x2019;s a markedly slower process. You&#x2019;re taking something that was iterated on for years and condensing its interface down to the essence while being careful not to permanently close the door on any legitimate use cases. It&#x2019;s also an opportunity and a license to rewrite the codebase over time and in a way that makes business sense.</p> <blockquote> <p><em><a href="https://en.wikipedia.org/wiki/Ship_of_Theseus" target="_blank" rel="noopener noreferrer">(Will it even be the same codebase once you&#x2019;ve fulfilled your vision, though!?)</a></em></p> </blockquote> <p>Rewiring the interfaces can give you an edge: with the beenfit of hindsight, its a lot easier to craft a consistent set of interfaces. You might make it so that <em>&#x2014; for instance &#x2014;</em> all components used to render lists of actions a user can perform take them in a standardized way, such as an array of button props. Such unified vision makes the library a breeze to use: consumers know to expect that virtually every component taking a list of actions expects button props. Consumers can thus write functions wiring business logic into the format your component library expects, and reuse them across different components and use cases.</p> <p>Synergy isn&#x2019;t something we could obtain by copying existing components and refactoring a bit. It&#x2019;s going to be made a whole lot better by starting again from scratch. Adoption of these new components gets trickier, but we&#x2019;ll get to that.</p> <h2 id="synergy">Synergy</h2> <p>One concrete example of synergy within the confines of the library we&#x2019;re building, can be told starting with our file upload button <code class="md-code md-code-inline">RyuButtonFiles</code>. This is a button users can click to upload files. It takes all the same props as a regular <code class="md-code md-code-inline">RyuButton</code>, plus a few more props that are specific to file upload buttons.</p> <p>Ryu also has a menu component. It renders a button and, when clicked, some menu items in a dropdown list. The button has a certain default look and feel, but you can customize the button props for it. Menu items also take an object that&#x2019;s quite similar to the props buttons take. Consuming <code class="md-code md-code-inline">RyuMenu</code> might look something like this:</p> <p>Now, suppose we want to mix the two. We want to render an upload button as a menu item. This is not a very common use case, and most component libraries I can think of don&#x2019;t have that use case in mind. Generally menu items can handle a click event and little else. Some are lucky to be able to handle links, but most try and get away with <code class="md-code md-code-inline">history.pushState</code> instead.</p> <p>Taking advantage of the <code class="md-code md-code-inline">as</code> prop <em>&#x2014; which we heavily constrain usage of, through TypeScript &#x2014;</em> from <code class="md-code md-code-inline">styled-components</code>, we can render a file upload button among our menu items. This prop essentially lets us render a component as something else, and even though Ryu doesn&#x2019;t expect you to use it often, we recognize it as a good way of supporting use cases that might otherwise go ignored.</p> <figure><img alt="File Menu UI" title="upload_menu_item.397bfe04.png" class src="https://images.ponyfoo.com/uploads/upload_menu_item.397bfe04-cc35f962cfc6490395580e300be06193.png"><figcaption>File Menu UI</figcaption></figure> <p>This renders a button that, when clicked shows a couple menu items, one of which is a label styled as a button styled as a menu item, that when clicked shows a system dialog for file uploads. </p> <p>In a similar vein, buttons as well as many other components in Ryu take an <code class="md-code md-code-inline">iconType</code>. Typically, libraries might take a string and render an icon represented by a certain identifier. Ryu takes the <code class="md-code md-code-inline">iconType</code> a little bit further, by allowing certain icon types to be animated. One such icon is a loading indicator. Instead of taking another prop for loading indicators, you&#x2019;d set <code class="md-code md-code-inline">iconType=&apos;loading&apos;</code>.</p> <p>For example, imagine how consumers could potentially come up with some sort of function to convert a <code class="md-code md-code-inline">RyuMenu</code> item to an upload button that behaves in the precise way they want, with loading and error state handling, and without compromising the core Ryu interface.</p> <p>It&#x2019;s hard to imagine how we&#x2019;d build a synergistic interface like this had we not decided to build the component library from scratch. Further, we think having a deep well of existing and maturely iterated components to draw inspiration from actually led to a far stronger interface than what we could&#x2019;ve built if we had the component library from day one. As I write these words, it is <a href="https://days.ramp.com/" target="_blank" rel="noopener noreferrer">day 973</a> and we&#x2019;re just getting started! </p> <h2 id="adoption">Adoption</h2> <p>Getting a core product to start using a component library built from scratch is no easy feat. Folks have an existing workflow, they probably know the components they usually work with intimately, and change is hard! When we add that the new library is a hard fork of existing components, that means every instance where we consume the old components needs to be ported over, and that may take a long time. You risk a split where neither approach fully wins, and you <a href="https://xkcd.com/927/" target="_blank" rel="noopener noreferrer">end up in a worse situation</a> than what your starting point was. To avoid this scenario you need to constantly reinforce the advantages, and the urgency, of adopting the new solution.</p> <p>The advantages should be manifold. This is why we&#x2019;ve decided to do a component library in the first place. Maintainability improvements, separation of concerns, more Pull Requests from infrequent contributors who aren&#x2019;t steeped in day-to-day front-end work, quicker iteration, more consistency, streamlined process, easier theming and accessibility, less code duplication, tighter coordination between Product, Design, and Engineering, and any other lofty goals we may have.</p> <p>There&#x2019;s many strategies we can pursue to drive adoption of a component library. We can pick a component and replace all instances with the newer one, eliminating the need for the older version altogether. We can choose a part of the application and refactor it to use the new stuff, and then it can serve as a point of reference. The most important aspect of adoption is that we don&#x2019;t keep on adding new debt, that is, new code shouldn&#x2019;t be using the old components unless there&#x2019;s a good reason to do so. The default mode should be using components in the new library, and over time we can remove references to old components.</p> <figure><img alt="Documentation in Action" class src="https://engineering.ramp.com/images/docs_demo.368ed9f7.gif"><figcaption>Documentation in Action</figcaption></figure> <p>At the initial stages, adoption is far more important than completeness. That is to say: it is better at this stage to only have 5 components that can only accomplish a subset of the use cases the original components supported, but have fully adopted them; than to have 25 components that aren&#x2019;t in use at all. Aligning the team behind adoption goals, rather than any one person taking it on on their own, is the best way to set the library up for success. To this end, complete and precise documentation is a must.</p> <h2 id="closing">Closing</h2> <p>We started this article talking about the timing to get started with a component library, talked about the trade-offs in creating one from scratch, and discussed the challenges along the way. As we continue to grow at an accelerating speed, we&#x2019;ll continue improving productivity by driving adoption of the component library across our applications (core app, our blogs, internal tools, etc.), and incorporating new components and designs along the way.</p> <p>Our small but burgeoning engineering team is always on the lookout for passionate folks. If you made it this far and are considering new roles, you can reach out to me on Twitter (feel free to DM me <a href="https://twitter.com/nzgb" target="_blank" rel="noopener noreferrer">@nzgb</a>) or apply to our <a href="https://boards.greenhouse.io/ramp/jobs/4644973002" target="_blank" rel="noopener noreferrer">Frontend Software Engineer</a> position directly. We have lots of interesting challenges and great chances for having a significant impact in the future of Ramp!</p> <p><a href="https://engineering.ramp.com/bootstrapping-a-ui-component-library" target="_blank" rel="noopener noreferrer"><em>Originally published on the Ramp Engineering blog.</em></a></p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/bootstrapping-a-ui-component-library</link><guid isPermaLink="true">https://ponyfoo.com/articles/bootstrapping-a-ui-component-library</guid><category><![CDATA[architecture]]></category><dc:creator><![CDATA[nico@ponyfoo.com (Nicolás Bevacqua)]]></dc:creator><pubDate>Thu, 16 Dec 2021 14:38:04 GMT</pubDate></item><item><title><![CDATA[React Data Survival Kit]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Handling data in React can be treacherous if you don&#x2019;t know your way around. Learn some common patterns for fetching, storing, and retrieving data in this guide to help you avoid messy code traps.</p><p>React&#x2019;s flexibility means you can handle data in a lot of different ways. This guide will teach you patterns for fetching, storing, and retrieving data in React without the stress of maintaining a complex system.</p><p>As part of <a href="https://cleverbeagle.com/" target="_blank" rel="noopener noreferrer">my mentorship business</a>, I have the unique opportunity to work with React daily across a wide range of projects. Teaching others how to build their own software products, I&#x2019;ve come to find that most problems in React can be solved with some very simple techniques.</p> <p>While some cases may call for a full-blown architecture using something like Redux (or other fanciness), a lot of the time React&#x2019;s built-in lifecycle methods and local state do the trick. In my opinion, the mark of a really great developer is one who can solve a problem with as few moving parts as possible. While it can feel great to build a monument to engineering in the form of a complex system, often it&#x2019;s just overkill.</p> <p>In this tutorial, I&#x2019;m going to share what I call my &#x201C;data survival kit:&#x201D; the most common patterns and hacks I use in my day-to-day work for managing data. By the time you finish, you&#x2019;ll have everything you need to build data-driven UIs that offer a polished user experience.</p> <h1 id="loading-data-via-hooks">Loading Data via Hooks</h1> <p>As most folks are building &#x201C;database apps,&#x201D; loading data is arguably one of the most common tasks to perform. Ultimately, where your data is coming from dictates your need for this. This pattern is best utilized in apps using a REST API or an RPC (remote procedure call) to fetch data.</p> <p>For example, if you&#x2019;re loading data via GraphQL, it&#x2019;s likely that you&#x2019;ll use this technique sparingly (e.g., when you need to fetch data programmatically as opposed to on page load), relying on tools like Apollo to load your data for you.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React, { useEffect, useState } from <span class="md-code-string">&apos;react&apos;</span>;

<span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Posts</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">const</span> [posts, setPosts] = useState([]);

  <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">getData</span><span class="md-code-params">()</span> </span>{
    fetch(<span class="md-code-string">&apos;https://jsonplaceholder.typicode.com/posts&apos;</span>).then(async (fetchedPosts) =&gt; {
      <span class="md-code-keyword">const</span> postsAsJson = await fetchedPosts.json();
      setPosts(postsAsJson);
    });
  }

  useEffect(() =&gt; {
    getData();
    <span class="md-code-keyword">const</span> pollForData = setInterval(() =&gt; getData(), <span class="md-code-number">5000</span>);
    <span class="md-code-keyword">return</span> () =&gt; {
      clearTimeout(pollForData);
    };
  }, []);

  <span class="md-code-keyword">return</span> (
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">h4</span>&gt;</span>Posts<span class="md-code-tag">&lt;/<span class="md-code-title">h4</span>&gt;</span>
      {posts.map(({ id, title, body }) =&gt; (
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span> <span class="md-code-attribute">key</span>=<span class="md-code-value">{id}</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">h3</span>&gt;</span>{title}<span class="md-code-tag">&lt;/<span class="md-code-title">h3</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">p</span>&gt;</span>{body}<span class="md-code-tag">&lt;/<span class="md-code-title">p</span>&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
      ))}
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  );
}

export default Posts;
</span></code></pre> <p>This example is relatively new but is ultimately just a refactor of a previous approach where you&#x2019;d use <code class="md-code md-code-inline">componentWillMount()</code> or <code class="md-code md-code-inline">componentDidMount()</code> to use React&#x2019;s new hooks feature. The idea is simple here: when our component loads into memory, go and fetch the data it will need and put it onto state.</p> <p>To make this work, we leverage the <code class="md-code md-code-inline">useEffect()</code> hook to say &#x201C;when our component loads, get our data and then set up a poll interval to refetch every five seconds.&#x201D; <code class="md-code md-code-inline">useEffect()</code> is essential, here, because side effects (e.g., fetching data) are not allowed in the body of a functional component due to their ability to produce &#x201C;confusing bugs and inconsistencies in the UI.&#x201D;</p> <p>The idea at play here is that <code class="md-code md-code-inline">useEffect()</code> will allow us to update the state of our functional component via the <code class="md-code md-code-inline">updatePosts()</code> method returned from our call to the <code class="md-code md-code-inline">useState()</code> hook. To prevent <code class="md-code md-code-inline">useEffect()</code> from running on every render of our component, we pass an empty array <code class="md-code md-code-inline">[]</code> as its second argument (this can contain values to conditionally fire <code class="md-code md-code-inline">useEffect()</code> when they change&#x2014;<a href="https://reactjs.org/docs/hooks-reference.html#conditionally-firing-an-effect" target="_blank" rel="noopener noreferrer">learn more here</a>).</p> <p>In order to clear out the interval when our component unmounts, <code class="md-code md-code-inline">useEffect()</code> accepts a return value of <a href="https://reactjs.org/docs/hooks-reference.html#cleaning-up-an-effect" target="_blank" rel="noopener noreferrer">a &#x201C;clean up&#x201D; function</a> (this function behaves similar to <code class="md-code md-code-inline">componentWillUnmount()</code> in a class). The end result is that our data will be fetched and put onto state when our component initially loads and again every five seconds until our component unmounts.</p> <p>The reason I like this pattern is that it makes data fetching easy to understand for beginners and veterans alike. It also makes tasks like polling/refetching to keep data up to date easy&#x2014;no need to call to an out-of-scope refetch function or Redux action. Technically speaking, too, this is handy because it helps us to avoid getting tangled up in global state&#x2014;something that should only be used if it&#x2019;s absolutely necessary.</p> <h1 id="autosaving-using-state">Autosaving Using State</h1> <p>As applications have evolved, features like &#x201C;autosave&#x201D; have become common enough for users to expect them. Fortunately, React&#x2014;and by proxy, it&#x2019;s state&#x2014;makes this sort of feature painless for us to implement.</p> <p>The way I like to accomplish this is by write input changes to state directly (a controlled component) and then after a delay, make a call to write to the database. Depending on the UI and the amount of data involved, I&#x2019;ll either send up a <code class="md-code md-code-inline">PATCH</code> for an individual field, or, a <code class="md-code md-code-inline">PUT</code> for the entire object it&#x2019;s part of.</p> <p>For the delay, I like to use this <code class="md-code md-code-inline">delay</code> function that I picked up a few years ago&#x2014;it&#x2019;s simple and has served its purpose well:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> delay = (() =&gt; {
  <span class="md-code-keyword">let</span> timer = <span class="md-code-number">0</span>;
  <span class="md-code-keyword">return</span> (callback, ms) =&gt; {
    clearTimeout(timer);
    timer = setTimeout(callback, ms);
  };
})();
</code></pre> <p>The basic premise of this function is that it&#x2019;s a self-clearing <code class="md-code md-code-inline">setTimeout()</code>. So, when it&#x2019;s called, if it&#x2019;s called again before it&#x2019;s timeout <code class="md-code md-code-inline">ms</code>, it clears itself to avoid overflowing JavaScript&#x2019;s call stack. After the specified delay in ms has passed, the function performs like a regular <code class="md-code md-code-inline">setTimeout</code>, executing the code it contains (i.e., once a user stops typing for 3000ms, <em>then</em> make the call).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">class</span> UserProfile extends React.Component {
  state = {};

  handleLiveUpdate = (event) =&gt; {
    <span class="md-code-keyword">const</span> { name, value } = event;
    <span class="md-code-keyword">const</span> { updateProfile } = <span class="md-code-keyword">this</span>.props;

    <span class="md-code-keyword">this</span>.setState({ [name]: value }, () =&gt; {
      <span class="md-code-comment">// After handleLiveUpdate has stopped being called for 3 seconds, call to update database.</span>
      delay(() =&gt; {
        <span class="md-code-comment">// Example #1: Updating the database via GraphQL mutation.</span>
        updateProfile({
          variables: {
            [name]: value,
          },
        });

        <span class="md-code-comment">// Example #2: Updating the database via Meteor Method.</span>
        Meteor.call(<span class="md-code-string">&apos;users.updateProfile&apos;</span>, { [name]: value }, (error) =&gt; {
          <span class="md-code-keyword">if</span> (error) {
            alert(error.reason);
          }
        });

        <span class="md-code-comment">// Example #3: Updating the database via HTTP PATCH.</span>
        fetch(<span class="md-code-string">&apos;https://app.com/api/users&apos;</span>, {
          method: <span class="md-code-string">&apos;PATCH&apos;</span>,
          body: <span class="md-code-built_in">JSON</span>.stringify({ [name]: value }),
        });
      }, <span class="md-code-number">3000</span>);
    });
  };

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">form</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">h4</span>&gt;</span>User Profile<span class="md-code-tag">&lt;/<span class="md-code-title">h4</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;firstName&quot;</span>&gt;</span>First Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;firstName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.firstName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleLiveUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;lastName&quot;</span>&gt;</span>Last Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;lastName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.lastName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleLiveUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;emailAddress&quot;</span>&gt;</span>Email Address<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;email&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;emailAddress&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.emailAddress}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleLiveUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">form</span>&gt;</span>
    );
  }
}
</span></code></pre> <p>The idea here is that as the user makes changes to the form we want to autosave, we immediately set state. Then, in the &#x201C;background,&#x201D; we utilize the <code class="md-code md-code-inline">delay()</code> function to say &#x201C;after they&#x2019;ve stopped typing, persist the current state in the database.&#x201D; The database part depends on how you handle data in your application.</p> <p>Because I spend a lot of my time <a href="https://cleverbeagle.com/pup" target="_blank" rel="noopener noreferrer">working in Meteor and GraphQL with the boilerplate I maintain, Pup</a>, I&#x2019;ll either use Meteor&#x2019;s <code class="md-code md-code-inline">Meteor.call()</code> convention to send my data to the server for storage, or, <a href="https://ponyfoo.com/articles/graphql-in-depth-what-why-and-how#understanding-mutations" target="_blank" rel="noopener noreferrer">call to a mutation</a> if I&#x2019;m relying on GraphQL. If I&#x2019;m building a mobile app, I&#x2019;ll rely on <code class="md-code md-code-inline">fetch()</code> or a library like <code class="md-code md-code-inline">axios()</code> to talk to my REST API.</p> <h1 id="using-local-storage-to-persist-unsaved-data">Using Local Storage to Persist Unsaved Data</h1> <p>One of the worst bits of UX is having a large form without a backup. As a user, there&#x2019;s nothing quite as disheartening as filling out a large form and accidentally hitting refresh to find all of your work is gone.</p> <p>Fortunately, the trick to getting around this is pretty simple (and utilizes a similar approach to the autosave example above). This pattern necessitates that our data live on our component&#x2019;s state, allowing us to maintain the illusion of a user&#x2019;s data being persisted without hitting a database.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import store from <span class="md-code-string">&apos;store&apos;</span>;

<span class="md-code-keyword">class</span> UserProfile extends React.Component {
  constructor(props) {
    super(props);
    <span class="md-code-keyword">this</span>.state = store.get(<span class="md-code-string">&apos;myApp.userProfile&apos;</span>) || {};
  }

  handleUpdate = (event) =&gt; {
    <span class="md-code-keyword">const</span> { name, value } = event;

    <span class="md-code-keyword">this</span>.setState({ [name]: value }, () =&gt; {
      delay(() =&gt; {
        store.set(<span class="md-code-string">&apos;myApp.userProfile&apos;</span>, <span class="md-code-keyword">this</span>.state);
      }, <span class="md-code-number">500</span>);
    });
  };

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">form</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">h4</span>&gt;</span>User Profile<span class="md-code-tag">&lt;/<span class="md-code-title">h4</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;firstName&quot;</span>&gt;</span>First Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;firstName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.firstName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;lastName&quot;</span>&gt;</span>Last Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;lastName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.lastName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;emailAddress&quot;</span>&gt;</span>Email Address<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;email&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;emailAddress&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.emailAddress}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">form</span>&gt;</span>
    );
  }
}
</span></code></pre> <p>This should look pretty familiar. Everything here is identical to the autosave approach save for a few small details.</p> <p>First, we&#x2019;ve introduced a library <code class="md-code md-code-inline">store</code> which will help us to get cross-browser access to local storage (or a comparable browser cache that&#x2019;s available to our user). Our usage of the library is limited to two calls: one when we load our component/page up for the first time and whenever a user changes their data.</p> <p>The first takes place in our component&#x2019;s <code class="md-code md-code-inline">constructor()</code> function. Here, we&#x2019;re setting our default state value relative to the current value of our local storage key (here, <code class="md-code md-code-inline">myApp.userProfile</code> is the name of the key we&#x2019;ve chosen to store our data in local storage). What we expect is that <code class="md-code md-code-inline">store.get()</code> will return an object containing properties that reflect the state values our UI requires.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">  handleUpdate = (event) =&gt; {
    <span class="md-code-keyword">const</span> { name, value } = event;

    <span class="md-code-keyword">this</span>.setState({ [name]: value }, () =&gt; {
      delay(() =&gt; {
        store.set(<span class="md-code-string">&apos;myApp.userProfile&apos;</span>, <span class="md-code-keyword">this</span>.state);
      }, <span class="md-code-number">500</span>);
    });
  };
</code></pre> <p>In order to get those values into local storage, we use a <code class="md-code md-code-inline">handleUpdate</code> method on our component that uses the same approach to our autosave pattern. Here, we immediately set the user&#x2019;s input on state and then after a short delay, we update the local storage value via <code class="md-code md-code-inline">store.set(&apos;myApp.userProfile&apos;, this.state)</code>.</p> <p>Two details here: notice that we&#x2019;ve reduced our delay significantly to <code class="md-code md-code-inline">500ms</code>. This is because it&#x2019;s far less expensive to hit our local storage than it is to make a trip to the server. Also, notice that we&#x2019;re setting the entirety of the current <code class="md-code md-code-inline">this.state</code> value onto local storage when we change any input. This, too, is inexpensive for performance and also saves us a messy <code class="md-code md-code-inline">constructor()</code> loaded with calls to <code class="md-code md-code-inline">store.get()</code> for each individual field.</p> <h1 id="reaching-into-a-child-component-via-refs">Reaching Into a Child Component via Refs</h1> <p>One of the bittersweet features of React is the ability to nest child components. It&#x2019;s bittersweet because while it makes composition easy&#x2014;bringing together multiple components in a cohesive UI&#x2014;it can make tasks like getting data out of child components a chore.</p> <p>A simple hack that I like to leverage for this is accessing child components via refs. While it&#x2019;s more common to pass a function via props to a child component that handles tracking the child&#x2019;s state on the parent (or at least, notifying the parent when its internal state changes), this can get messy and cumbersome.</p> <p>Instead, if all we care to know is the current internal state of a child component, refs make our lives a hell of a lot easier. Let&#x2019;s consider the example of a job application. We have some basic form fields we&#x2019;d like to grab along with two lists: the candidates strengths and weaknesses.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;

<span class="md-code-keyword">class</span> List extends React.Component {
  state = {
    items: [],
  };

  handleRemoveItem = (id) =&gt; {
    <span class="md-code-keyword">this</span>.setState(({ items }) =&gt; ({
      items: items.filter((item) =&gt; item.id !== id),
    }));
  };

  handleAddItem = (event) =&gt; {
    event.persist(); <span class="md-code-comment">// Use event.persist() so we don&apos;t lose React synthetic event in nested function below.</span>
    <span class="md-code-keyword">this</span>.setState(({ items }) =&gt; ({
      <span class="md-code-comment">// randomIdGenerator() is used for example here and doesn&apos;t exist.</span>
      items: [...items, { id: randomIdGenerator(), item: event.item.value }],
    }));
  };

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
        {this.state.items.map(({ id, item }) =&gt; (
          <span class="md-code-tag">&lt;<span class="md-code-title">li</span> <span class="md-code-attribute">key</span>=<span class="md-code-value">{id}</span>&gt;</span>
            {item}
            <span class="md-code-tag">&lt;<span class="md-code-title">button</span> <span class="md-code-attribute">onClick</span>=<span class="md-code-value">{()</span> =&gt;</span> this.handleRemoveItem(id)}&gt;
              <span class="md-code-tag">&lt;<span class="md-code-title">i</span> <span class="md-code-attribute">className</span>=<span class="md-code-value">&quot;fas fa-remove&quot;</span> /&gt;</span>
            <span class="md-code-tag">&lt;/<span class="md-code-title">button</span>&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">li</span>&gt;</span>
        ))}
        <span class="md-code-tag">&lt;<span class="md-code-title">form</span> <span class="md-code-attribute">onSubmit</span>=<span class="md-code-value">{this.handleAddItem}</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;item&quot;</span> /&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">button</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;submit&quot;</span>&gt;</span>Add Item<span class="md-code-tag">&lt;/<span class="md-code-title">button</span>&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">form</span>&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
    );
  }
}
</span></code></pre> <p>To manage those two lists, we use a nested component with its own state called <code class="md-code md-code-inline">&lt;List /&gt;</code>. Internally, the component gives the applicant an input to add as many list items as they choose. As a standalone component, this doesn&#x2019;t present us with any issues.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import List from <span class="md-code-string">&apos;./path/to/List&apos;</span>;

<span class="md-code-keyword">class</span> JobApplication extends React.Component {
  state = {
    firstName: <span class="md-code-string">&apos;&apos;</span>,
    lastName: <span class="md-code-string">&apos;&apos;</span>,
    emailAddress: <span class="md-code-string">&apos;&apos;</span>,
  };

  handleSubmitApplication = (event) =&gt; {
    <span class="md-code-keyword">const</span> { submitApplication } = <span class="md-code-keyword">this</span>.props;

    <span class="md-code-comment">// Example call to a GraphQL mutation here to perform the submission.</span>
    submitApplication({
      variables: {
        ...this.state,
        strengths: <span class="md-code-keyword">this</span>.strengths.state.items,
        weaknesses: <span class="md-code-keyword">this</span>.weaknesses.state.items,
      },
    });
  };

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">React.Fragment</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">h1</span>&gt;</span>Job Application<span class="md-code-tag">&lt;/<span class="md-code-title">h1</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">form</span> <span class="md-code-attribute">onSubmit</span>=<span class="md-code-value">{this.handleSubmitApplication}</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;firstName&quot;</span>&gt;</span>First Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;firstName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.firstName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;lastName&quot;</span>&gt;</span>Last Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;lastName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.lastName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;emailAddress&quot;</span>&gt;</span>Email Address<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;email&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;emailAddress&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.emailAddress}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span>&gt;</span>What are some of your strengths?<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">List</span> <span class="md-code-attribute">ref</span>=<span class="md-code-value">{strengths</span> =&gt;</span> (this.strengths = strengths)} /&gt;
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span>&gt;</span>What are some of your weaknesses?<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">List</span> <span class="md-code-attribute">ref</span>=<span class="md-code-value">{weaknesses</span> =&gt;</span> (this.weaknesses = weaknesses)} /&gt;
          <span class="md-code-tag">&lt;<span class="md-code-title">button</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;submit&quot;</span>&gt;</span>Submit Job Application<span class="md-code-tag">&lt;/<span class="md-code-title">button</span>&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">form</span>&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">React.Fragment</span>&gt;</span>
    );
  }
}
</span></code></pre> <p>Where things can potentially get messy is when we render the <code class="md-code md-code-inline">&lt;List /&gt;</code> component inside of a parent. Here, <code class="md-code md-code-inline">&lt;JobApplication /&gt;</code> represents that parent. In the <code class="md-code md-code-inline">render()</code> we can see two instances of <code class="md-code md-code-inline">&lt;List /&gt;</code> being generated.</p> <p>Traditionally, we could add a prop to <code class="md-code md-code-inline">&lt;List /&gt;</code> called <code class="md-code md-code-inline">onUpdate()</code> that was called internally by <code class="md-code md-code-inline">&lt;List /&gt;</code> passing up the current items. Where this becomes problematic is in having to track the list&#x2019;s state both internally as well as on the parent (or having the parent feed the child its data&#x2014;no bueno unless we need to load data from the database).</p> <p>To simplify this, we add a <code class="md-code md-code-inline">ref</code> to each instance of the <code class="md-code md-code-inline">&lt;List /&gt;</code>, assigning it back to our <code class="md-code md-code-inline">&lt;JobApplication /&gt;</code> component as either <code class="md-code md-code-inline">this.strengths</code> or <code class="md-code md-code-inline">this.weaknesses</code>. What&#x2019;s great about this is that now, we have direct access to these components from within <code class="md-code md-code-inline">&lt;JobApplication /&gt;</code>.</p> <p>When our applicant submits the form, all we need to do get its items is call to either <code class="md-code md-code-inline">this.strengths.state.items</code> or <code class="md-code md-code-inline">this.weaknesses.state.items</code>.</p> <h2 id="bonus-controlling-a-child-via-the-parent">Bonus: Controlling a Child via the Parent</h2> <p>You may be wondering, does this mean I can control the child component via refs as well? Yes. You want to be careful with this, however, as it can produce unexpected side effects. For example, when it comes to updating a component&#x2019;s internal state relative to a parent&#x2019;s data, it&#x2019;s best to pass changes via props.</p> <p>Sometimes, though, this isn&#x2019;t always feasible or wanted. For example, consider our example above. After the application is submitted, let&#x2019;s assume we want to &#x201C;reset&#x201D; the form. Because our <code class="md-code md-code-inline">&lt;List /&gt;</code> components maintain their state internally, this means we need to manipulate their state from <code class="md-code md-code-inline">&lt;JobApplication /&gt;</code>.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import List from <span class="md-code-string">&apos;./path/to/List&apos;</span>;

<span class="md-code-keyword">class</span> JobApplication extends React.Component {
  state = {
    firstName: <span class="md-code-string">&apos;&apos;</span>,
    lastName: <span class="md-code-string">&apos;&apos;</span>,
    emailAddress: <span class="md-code-string">&apos;&apos;</span>,
  };

  handleSubmitApplication = (event) =&gt; {
    <span class="md-code-keyword">const</span> { submitApplication } = <span class="md-code-keyword">this</span>.props;

    <span class="md-code-comment">// Example call to a GraphQL mutation here to perform the submission.</span>
    submitApplication({
      variables: {
        ...this.state,
        strengths: <span class="md-code-keyword">this</span>.strengths.state.items,
        weaknesses: <span class="md-code-keyword">this</span>.weaknesses.state.items,
      },
    }).then(() =&gt; {
      <span class="md-code-keyword">this</span>.strengths.setState({ items: [] });
      <span class="md-code-keyword">this</span>.weaknesses.setState({ items: [] });
    });
  };

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">React.Fragment</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">h1</span>&gt;</span>Job Application<span class="md-code-tag">&lt;/<span class="md-code-title">h1</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">form</span> <span class="md-code-attribute">onSubmit</span>=<span class="md-code-value">{this.handleSubmitApplication}</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;firstName&quot;</span>&gt;</span>First Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;firstName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.firstName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;lastName&quot;</span>&gt;</span>Last Name<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;text&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;lastName&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.lastName}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">label</span> <span class="md-code-attribute">htmlFor</span>=<span class="md-code-value">&quot;emailAddress&quot;</span>&gt;</span>Email Address<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
            <span class="md-code-tag">&lt;<span class="md-code-title">input</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;email&quot;</span> <span class="md-code-attribute">name</span>=<span class="md-code-value">&quot;emailAddress&quot;</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{this.state.emailAddress}</span> <span class="md-code-attribute">onChange</span>=<span class="md-code-value">{this.handleUpdate}</span> /&gt;</span>
          <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span>&gt;</span>What are some of your strengths?<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">List</span> <span class="md-code-attribute">ref</span>=<span class="md-code-value">{strengths</span> =&gt;</span> (this.strengths = strengths)} /&gt;
          <span class="md-code-tag">&lt;<span class="md-code-title">label</span>&gt;</span>What are some of your weaknesses?<span class="md-code-tag">&lt;/<span class="md-code-title">label</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">List</span> <span class="md-code-attribute">ref</span>=<span class="md-code-value">{weaknesses</span> =&gt;</span> (this.weaknesses = weaknesses)} /&gt;
          <span class="md-code-tag">&lt;<span class="md-code-title">button</span> <span class="md-code-attribute">type</span>=<span class="md-code-value">&quot;submit&quot;</span>&gt;</span>Submit Job Application<span class="md-code-tag">&lt;/<span class="md-code-title">button</span>&gt;</span>
        <span class="md-code-tag">&lt;/<span class="md-code-title">form</span>&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">React.Fragment</span>&gt;</span>
    );
  }
}
</span></code></pre> <p>Here, when our applicant submits the form, as soon as we get an &#x201C;all good&#x201D; back from the database, we call to <code class="md-code md-code-inline">this.strengths.setState()</code> and <code class="md-code md-code-inline">this.weaknesess.setState()</code> to reset their internal state.</p> <p>Again, use this wisely&#x2014;I consider this a hack which means it should be applied with caution and ample experimentation/testing.</p> <h2 id="conclusion">Conclusion</h2> <p>Using data in React doesn&#x2019;t need to be complicated. In fact, one of the joys of using React is that it can help you produce interactive UIs with very little effort. If you&#x2019;re already committed to React, it&#x2019;s worth considering how you might simplify your own use of it. More often than not, the problems I see in React applications involve unnecessarily complex data patterns.</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/react-data-survival-kit</link><guid isPermaLink="true">https://ponyfoo.com/articles/react-data-survival-kit</guid><category><![CDATA[react]]></category><category><![CDATA[data]]></category><category><![CDATA[state]]></category><category><![CDATA[hooks]]></category><category><![CDATA[refs]]></category><category><![CDATA[autosave]]></category><dc:creator><![CDATA[ryan.glover@cleverbeagle.com (Ryan Glover)]]></dc:creator><pubDate>Mon, 03 Jun 2019 12:20:42 GMT</pubDate></item><item><title><![CDATA[Discovering patterns with React hooks]]></title><description><![CDATA[<div class="f-core md-markdown"><p>One of the things I enjoy the most about coding is discovering patterns. Being aware and mindful of the patterns emerging in your codebase can make it easier to keep that codebase consistent, readable and easy to navigate. Most patterns will remain unspoken, and some, that prove notably elegant, are named and promoted as best practices. Others might even be candidates for a basis of a new abstraction, although I recommend restraint as it&#x2019;s worth remembering that <a href="https://www.joelonsoftware.com/2002/11/11/the-law-of-leaky-abstractions" target="_blank" rel="noopener noreferrer">abstractions are expensive</a>.</p><blockquote> <p>If you&#x2019;re not familiar with the topic or React hooks, there is plenty of <a href="https://medium.com/@dan_abramov/making-sense-of-react-hooks-fdbde8803889" target="_blank" rel="noopener noreferrer">introductory</a> as well as <a href="https://www.robinwieruch.de/react-hooks" target="_blank" rel="noopener noreferrer">in-depth materials</a> out there. I can also shamelessly recommend <a href="https://www.youtube.com/watch?v=Vizk0clZHeI" target="_blank" rel="noopener noreferrer">my talk on hooks</a> from a recent React Copenhagen meetup. What follows assume at least some understanding of what hooks are and how to use them.</p> </blockquote><h2 id="pattern-extinction-level-event">Pattern extinction level event</h2> <p>Discovering new patterns is relatively rare when working with established technologies, but every now and then a new idea comes up that stirs things up within some ecosystem. Enter React hooks. Hooks enable a drastically different way to build stateful components in React, which means that they also invalidate <sup><a id="footnote-1" href="#footnote-1-marker" class="md-footnote-ref">1</a></sup> a lot of the established patterns. However, being an exceptionally well thought-through and composition-friendly abstraction, hooks enabled new, elegant patterns to emerge.</p> <p>Over the last few months, I&#x2019;ve been building apps with hooks and I&#x2019;ve been having a lot of fun discovering these patterns. I&#x2019;d like to share one, that I&#x2019;ve grown especially fond of. For the lack of a better name, and for sole the purpose of this blog post, let&#x2019;s call it the Choco pattern (from Context, HOok, COpomonent). The pattern comes useful when you need a UI widget which is top-level, centralised and unique across the application and needs to expose some functionality to the rest of the components in the app. It might sound vague at the moment, so let&#x2019;s try to go through solving a concrete problem where I noticed the Choco pattern manifest itself.</p> <h2 id="exhibit-a-feature-toggles">Exhibit A: Feature toggles</h2> <p>Let&#x2019;s try to build a simple feature toggle functionality for a React app. There should be UI where the user can toggle features on and off and we want to expose these settings to any component interested. We can identify parts we will need:</p> <ol> <li>a context provider exposing the feature toggles anywhere in the component tree,</li> <li>a component rendering the UI for toggling features,</li> <li>a place to store the feature toggle state and the associated logic.</li> </ol> <p>Our first attempt could look like this:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-feature-toggles?path=script.jsx&amp;previewSize=0" alt="hooks-feature-toggles on Glitch" height="417px" width="100%"> </iframe> <p>This code is fairly simple and works so we could stop here. However, there is a big opportunity for refactoring, by moving all the code related to the &#x201C;feature toggle concern&#x201D; to a separate module, instead of mixing it with the rest of our app. Let&#x2019;s do just that.</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-feature-toggles-2?path=feature-toggle.jsx&amp;previewSize=0" alt="hooks-feature-toggles-2 on Glitch" width="100%" height="431px"> </iframe> <p>The biggest change here is extracting the state and its related logic into a custom hook. We can now clearly see the Choco pattern here, with the Context, HOok, and COpomonent exported from the new module.</p> <p>I made a choice here to keep the <code class="md-code md-code-inline">initialFeatures</code> dictionary with the application code instead of moving it to the newly created module. One could argue either way, but by keeping that dictionary out of the feature toggle module and dependency-injecting it, we make that module completely generic and a candidate for possible extraction into a library.</p> <p>Again, we could easily end the refactoring here. In fact, that&#x2019;s the level of decoupling I&#x2019;d recommend for in this scenario (more on that later). We can, however, take it one step further and introduce an abstraction to hide some of the implementation details and reduce the API surface of the module. Here is one way to do this:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-feature-toggles-3?path=feature-toggle.jsx&amp;previewSize=0" alt="hooks-feature-toggles-3 on Glitch" width="100%" height="431px"> </iframe> <p>The biggest difference to notice is that the fact we&#x2019;re using the native React context is now an implementation detail, not exposed to the rest of the app. By adding our own abstraction on top of it we also gained a more fine-grain control over what properties of the context are exposed. In this case, we could expose only the feature toggle dictionary, while keeping the function to toggle them private.</p> <p>Another thing to notice is that by wrapping the context provider in a component, we had an opportunity to render the <code class="md-code md-code-inline">FeatureToggler</code> in that component and take that responsibility away from the module&#x2019;s consumer. This might be a viable option if we decide to e.g. render the toggler in a portal to make it overlay the page after the user presses some specific key combination. However, in this example I chose not to - this way is much more flexible as the consumer can choose where and how to render the toggler.</p> <p>What&#x2019;s interesting is that we kept the same type of exports from the feature toggle module (Context, HOok, and COpomonent), but they are consumed in a different way. The hook is now taking care of exposing the features to the components. The context (or more precisely the wrapped context provider) now also encapsulates the state and related logic.</p> <p>Did we improve this code in comparison to the previous iteration? I think the answer depends on how we plan to use it. If we decided to extract the feature toggle module into a small library, then that last version of the code is most likely a better choice. It would be easier to document and use the API with a smaller exposed surface. Hiding the implementation details might also enable adding more functionality later on, without introducing breaking changes.</p> <p>However, if we keep it as a module within the codebase the abstraction creates an unnecessary indirection that the next developer would need to unpack to understand. We created a black box that would need to be explored as opposed to the previous version, which while verbose, used the core React APIs most likely already familiar. Since we don&#x2019;t extract and version the module, breaking changes are not a big deal as we could quickly make necessary adjustments in the places where the module is consumed.</p> <h2 id="exhibit-b-toast-notifications">Exhibit B: Toast notifications</h2> <p>It wouldn&#x2019;t be much of a pattern if it didn&#x2019;t repeat. To see how it manifests again and again in this kind of problems, let&#x2019;s look at one more example. We are going to build a simplified toast notification system. We want to be able to ask for a notification anywhere in our component tree and then show it in a global bar. Again, let&#x2019;s identify what pieces we are going to need:</p> <ol> <li>a context provider exposing a function that can be used to ask for a toast,</li> <li>a component rendering the toast bar,</li> <li>a place to store the notifications state and related logic</li> </ol> <p>We already know what to expect so let&#x2019;s skip the first draft and go directly to the version with a separate module:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-toasts?path=feature-toggle.jsx&amp;previewSize=0" alt="hooks-toasts on Glitch" width="100%" height="431px"> </iframe> <p>It&#x2019;s our Choco pattern on full display! And as in the previous example, we can refactor this to make the API cleaner and more controlled. It could look like this:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-toasts-2?path=toasts.jsx&amp;previewSize=0" alt="hooks-toasts-2 on Glitch" width="100%" height="431px"> </iframe> <p>As in the feature toggle example, I chose to only expose some part of the context value to the component tree through the custom hook. In this case, the array of toasts remains private, while the functions to add and remove toasts are exposed.</p> <h2 id="exhibit-c-user-guide">Exhibit C: User guide</h2> <p>To cement the pattern further, let&#x2019;s look at one last example. We will build user guide functionality. We want to add a help button in some places in our app, which when clicked, opens a user guide widget that explains the part of the interface in question. You know the drill by now, let&#x2019;s see what we&#x2019;re going to need:</p> <ol> <li>a context provider exposing the help icon button component</li> <li>a component rendering the user guide UI</li> <li>a place to store state (i.e. which piece of the interface the user asked for) and related logic</li> </ol> <p>Our first iteration could look like this:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-user-guide?path=user-guide.jsx&amp;previewSize=0" alt="hooks-user-guide on Glitch" width="100%" height="431px"> </iframe> <p>Hopefully, the structure is intuitive and familiar by now. The only significant difference is the type of functionality exposed to the other components. In the case of the guide, we choose to expose a component, while in the previous examples it was a method or state data. Anything goes!</p> <p>Following the previously established procedure, the refactored version of the same code could look like this:</p> <iframe allow="geolocation; microphone; camera; midi; vr; encrypted-media" src="https://glitch.com/embed/#!/embed/hooks-user-guide-2?path=user-guide.jsx&amp;previewSize=0" alt="hooks-user-guide-2 on Glitch" width="100%" height="431px"> </iframe> <h2 id="spot-the-pattern">Spot the pattern</h2> <p>Of course, I slightly cheated with the way I presented the refactoring evolution here. I already knew the pattern and made the examples follow it from the very beginning. In fact, when I first got to implement these features using hooks, I didn&#x2019;t immediately see the Choco pattern and my initial implementation attempts were chaotic and inconsistent. But with time, I started to see the same code paths and elements showing up repeatedly and I was able to streamline and clean up my implementations.</p> <p>Discovering and embracing patterns can be a powerful technique that can make penetrating and understanding a codebase easier. I&#x2019;m always on a lookout for concerns and features that share similar requirements and characteristics and try to implement them in a consistent way, without introducing abstractions prematurely.</p> <p>I&#x2019;m really eager to find out what other concerns are candidates for using this pattern and what other patterns using hooks will surface. Let me know what you think!</p> <div class="md-footnotes"> <div id="footnote-1-marker" class="md-footnote"><a href="#footnote-1" class="md-footnote-anchor">1</a><div><p>Of course, hooks being backward-compatible, you can still use old patterns in React if you stick to the class component API.</p> </div></div> </div></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/discovering-patterns-with-react-hooks</link><guid isPermaLink="true">https://ponyfoo.com/articles/discovering-patterns-with-react-hooks</guid><category><![CDATA[react]]></category><category><![CDATA[patterns]]></category><dc:creator><![CDATA[m@peka.la (Maciek Pekala)]]></dc:creator><pubDate>Mon, 13 May 2019 12:15:55 GMT</pubDate></item><item><title><![CDATA[Disguise Driven Testing: Jest Mocks In Depth — Part 2]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Mocks are a great way of preventing AJAX calls in tests, but they can also help you isolate side effects and impurities that can create complicated tests.</p> <p>As you <a href="https://ponyfoo.com/articles/disguise-driven-testing-jest-mocks-in-depth" aria-label="Disguise-Driven Testing: Jest Mocks in Depth &#x2014; Part 1 on Pony Foo">learned in Part 1</a>, mocks are a great way to handle external data or any data that is likely to change. Mocking external data will likely be your most common use case and for a good reason. You want your tests to stick as closely to your code as possible this includes all dependencies. Still, there are times when a dependency creates a testing specific problem. In other words, the code works as you would want it to in production, but in a testing environment can make consistent, predictable tests hard or nearly impossible.</p> <p>Some of your third party code may have side effects or some form of impurity that will complicate your testing. Maybe the code has certain expectations of the DOM. Maybe it will change slightly depending on the order of operations. In all cases, the code is out of your control, but you need it to be predictable.</p><h3 id="creating-predictable-outcomes-with-mocks">Creating Predictable Outcomes with Mocks</h3> <p>Suppose you have an application that manages employees or users. You need to make a simple function that will create a new employee, but you want to make sure that the employee always contains certain fields and will have a unique id.</p> <p>Your code will look something like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">createEmployee</span><span class="md-code-params">(details)</span> </span>{
  <span class="md-code-keyword">return</span> {
    name: <span class="md-code-string">&apos;&apos;</span>,
    position: <span class="md-code-string">&apos;&apos;</span>,
    ...details,
    key: uniqueId()
  }
}
</code></pre> <p>In this case, you are using the Lodash method <code class="md-code md-code-inline">uniqueId</code> to, <em>well</em>, create a unique ID. Specifically you are creating a key that you can use if you do any manipulations on an array of employees. The key, though, is a minor thing.</p> <p>For the most part, your tests should focus on other aspects of your code such as ensuring default fields while allowing those fields to override. Here are two simple tests.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils&apos;</span>

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should create a blank employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee()
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  it(<span class="md-code-string">&apos;should create use existing details when creating employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ name: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;2&apos;</span>
    }

    expect(blank).toEqual(expected)
  })
})
</code></pre> <p>Notice anything strange in the expectations? In the first case, the key is <code class="md-code md-code-inline">&apos;1&apos;</code> in the next test it&#x2019;s <code class="md-code md-code-inline">&apos;2&apos;</code>. Here&#x2019;s where the problems start to sneak in. Every time you add a test, you need to iterate the <code class="md-code md-code-inline">key</code>. The <code class="md-code md-code-inline">uniqueId</code> function is doing exactly what you would want in production, but in the testing case, it&#x2019;s creating a situation where your tests <em>always</em> have to run in the same order.</p> <p>Suppose you needed to change the function slightly to handle cases where you get an object with a field of <code class="md-code md-code-inline">firstName</code> and you want to use as the <code class="md-code md-code-inline">name</code> field:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">createEmployee</span><span class="md-code-params">(details = {})</span> </span>{
  <span class="md-code-keyword">const</span> { firstName: name, ...rest } = details

  <span class="md-code-keyword">return</span> {
    name: name || <span class="md-code-string">&apos;&apos;</span>,
    position: <span class="md-code-string">&apos;&apos;</span>,
    ...rest,
    key: uniqueId()
  }
}
</code></pre> <p>Your tests now look like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils.next&apos;</span>

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should create a blank employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee()
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  it(<span class="md-code-string">&apos;should create use existing details when creating employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ name: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;2&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  it(<span class="md-code-string">&apos;should convert firstName to name&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ firstName: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;3&apos;</span>
    }

    expect(blank).toEqual(expected)
  })
})
</code></pre> <p>What happens if you change the order? Suppose you wanted to only run the third test:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils.next&apos;</span>

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  <span class="md-code-comment">// &#x2026; other tests &#x2026;</span>

  it.only(<span class="md-code-string">&apos;should convert firstName to name&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ firstName: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;3&apos;</span>
    }

    expect(blank).toEqual(expected)
  })
})
</code></pre> <p>You&#x2019;d get an error. But it&#x2019;s not because your code is wrong. The order has just changed.</p> <pre class="md-code-block"><code class="md-code md-lang-diff">  expect(<mark class="md-mark md-code-mark">received</mark>).toEqual(<mark class="md-mark md-code-mark">expected</mark>)

  Expected value to equal:
<span class="md-code-deletion">-   {&quot;key&quot;: &quot;3&quot;, &quot;name&quot;: &quot;Bill&quot;, &quot;position&quot;: &quot;&quot;}</span>
  Received:
<span class="md-code-addition">+   {&quot;key&quot;: &quot;1&quot;, &quot;name&quot;: &quot;Bill&quot;, &quot;position&quot;: &quot;&quot;}</span>

  Difference:

<span class="md-code-deletion">-   Expected:</span>
<span class="md-code-addition">+   Received:</span>

    Object {
<span class="md-code-deletion">-     &quot;key&quot;: &quot;3&quot;,</span>
<span class="md-code-addition">+     &quot;key&quot;: &quot;1&quot;,</span>
      &quot;name&quot;: &quot;Bill&quot;,
      &quot;position&quot;: &quot;&quot;,
    }

  &#x2026;
</code></pre> <p>So how can you fix it? In this case, you have a couple of options.</p> <p>The easiest option is to override it inside the test itself. By using <code class="md-code md-code-inline">mockImplementation</code>.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils.next&apos;</span>
import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

uniqueId.mockImplementation(() =&gt; <span class="md-code-string">&apos;1&apos;</span>)

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should create a blank employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee()
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  it(<span class="md-code-string">&apos;should create use existing details when creating employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ name: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  it(<span class="md-code-string">&apos;should convert firstName to name&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee({ firstName: <span class="md-code-string">&apos;Bill&apos;</span> })
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;Bill&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })
})
</code></pre> <p>Notice what&#x2019;s happening. First, you import the code with <code class="md-code md-code-inline">import uniqueId from &apos;lodash/uniqueId&apos;</code>, then you add a <code class="md-code md-code-inline">mockImplementation</code> to the function you want to control. Jest will now use the <code class="md-code md-code-inline">mockImplementation</code> rather than the actual code anytime you call the function.</p> <p>In other words, you now have a function that returns the result you want everytime. After that, you can write each test expect the exact same key. After all, you don&#x2019;t really care what the key is, you just want it to be there.</p> <p>Another option is to just define the return value using <code class="md-code md-code-inline">mockReturnValue</code>. The steps are the same, but instead of returning a function, you just declare the desired return value.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils.next&apos;</span>
import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

uniqueId.mockReturnValue(<span class="md-code-string">&apos;2&apos;</span>)

<span class="md-code-comment">// Tests are the same</span>
</code></pre> <p>Those are both perfectly good solutions. You may reach for <code class="md-code md-code-inline">mockImplementation</code> in situations where you need to check parameters before you decide what to return.</p> <p>The problem with both solutions is that you have a little extra boilerplate for every single test suite that uses the <code class="md-code md-code-inline">uniqueId</code>. To solve that, you can use a similar technique to mocking out the API.</p> <p>Create a new directory called <code class="md-code md-code-inline">__mocks__</code> at the same level as your <code class="md-code md-code-inline">node_modules</code> directory. This will almost certainly be the root of your project. Inside <code class="md-code md-code-inline">__mocks__</code> create a file structure that emulates your import. In this case, you need a Lodash directory that contains a <code class="md-code md-code-inline">uniqueId.js</code> file.</p> <pre class="md-code-block"><code class="md-code">| __mocks__
| + lodash
|   + uniqueId.js
| node_modules
</code></pre> <p>Inside, <code class="md-code md-code-inline">uniqueId.js</code> export either your <code class="md-code md-code-inline">mockImplementation</code> or your <code class="md-code md-code-inline">mockReturnValue</code>. The only difference is that you have to create a <code class="md-code md-code-inline">jest.fn()</code> first. It&#x2019;s not strictly required that you return <code class="md-code md-code-inline">jest.fn()</code> you can just return any function. However, by returning a mocked <code class="md-code md-code-inline">jest.fn()</code> you have more options to change the return value (more on that in a bit).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-keyword">default</span> jest.fn().mockImplementation(() =&gt; <span class="md-code-string">&apos;1&apos;</span>)
</code></pre> <p>Now that you have mocked the function at the <code class="md-code md-code-inline">import</code> level, you don&#x2019;t need to do anything in your actual test. The mock is detected for you.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, } from <span class="md-code-string">&apos;./utils.next&apos;</span>

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should create a blank employee&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank = createEmployee()
    <span class="md-code-keyword">const</span> expected = {
      name: <span class="md-code-string">&apos;&apos;</span>,
      position: <span class="md-code-string">&apos;&apos;</span>,
      key: <span class="md-code-string">&apos;1&apos;</span>
    }

    expect(blank).toEqual(expected)
  })

  <span class="md-code-comment">// Other tests</span>

})
</code></pre> <h3 id="changing-on-the-fly">Changing on the fly</h3> <p>Of course, if you want to change if for an individual test you can still do so. Suppose you had a helper function that sorts the employees by key. This would, essentially, reset the order.</p> <p>The function is pretty straightforward:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">sortEmployees</span><span class="md-code-params">(employees)</span> </span>{
  <span class="md-code-keyword">return</span> [...employees].sort((a,b) =&gt; <span class="md-code-built_in">Number</span>(a.key) - <span class="md-code-built_in">Number</span>(b.key))
}
</code></pre> <p>This makes testing a little more tricky. Remember, you just mocked the function so it always returns the same value. And that is good for all the previous tests, but now you need the few <em>different</em> unique ids.</p> <p>Fortunately, once you have it mocked, it&#x2019;s easy to make changes at runtime&#x2026; As a reminder here&#x2019;s the mock in <code class="md-code md-code-inline">__mocks__/lodash/uniqueId.js</code>:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-keyword">default</span> jest.fn().mockImplementation(() =&gt; <span class="md-code-string">&apos;1&apos;</span>)
</code></pre> <p>The important thing is that you are exporting a jest function. That means you can import that mocked function and change it on the fly.</p> <p>First, import <code class="md-code md-code-inline">uniqueId</code> into your test:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>
</code></pre> <p>Now, in your test anytime you want to change the value, you declare what you want it to return. As a safety, use the method <code class="md-code md-code-inline">mockImplementationOnce</code> this will ensure that the method will only return the alternate value, well, once. Then it will return to the original mock implementation. This will prevent some confusing bugs from sneaking into your code.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

uniqueId.mockImplementationOnce(() =&gt; <span class="md-code-string">&apos;2&apos;</span>)

uniqueId()
<span class="md-code-comment">// 2</span>

uniqueId()
<span class="md-code-comment">// 1</span>
</code></pre> <p>With this power you can create a few employees with different unique ids. After that the test is simple:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { createEmployee, sortEmployees } from <span class="md-code-string">&apos;./utils.next&apos;</span>
import uniqueId from <span class="md-code-string">&apos;lodash/uniqueId&apos;</span>

describe(<span class="md-code-string">&apos;createEmployee&apos;</span>, () =&gt; {
  <span class="md-code-comment">// &#x2026; tests</span>

  it(<span class="md-code-string">&apos;should sort employees by id&apos;</span>, () =&gt; {
    <span class="md-code-keyword">const</span> blank1 = createEmployee({ name: <span class="md-code-string">&apos;Olivia&apos;</span>})

    uniqueId.mockImplementationOnce(() =&gt; <span class="md-code-string">&apos;2&apos;</span>)
    <span class="md-code-keyword">const</span> blank2 = createEmployee({ firstName: <span class="md-code-string">&apos;Xander&apos;</span>})

    uniqueId.mockImplementationOnce(() =&gt; <span class="md-code-string">&apos;3&apos;</span>)
    <span class="md-code-keyword">const</span> blank3 = createEmployee({ name: <span class="md-code-string">&apos;Bill&apos;</span> })

    <span class="md-code-keyword">const</span> expected = [<span class="md-code-string">&apos;Olivia&apos;</span>, <span class="md-code-string">&apos;Xander&apos;</span>, <span class="md-code-string">&apos;Bill&apos;</span>]
    <span class="md-code-keyword">const</span> employees = [blank2, blank3, blank1]

    <span class="md-code-keyword">const</span> mapped = sortEmployees(employees).map(employee =&gt; employee.name)

   expect(mapped).toEqual(expected)
  })

  <span class="md-code-comment">// &#x2026; more tests</span>
})
</code></pre> <p>In this situation, you are creating different unique ids for only this one specific set of assertions. Everything else falls back to the predictable unique id.</p> <h3 id="ignoring-black-boxes">Ignoring Black Boxes</h3> <p>There&#x2019;s more ways you can use <code class="md-code md-code-inline">mockImplementation</code> to change or modify individual library methods. But occasionally, you want to completely remove a piece of external code. You may have a heavy piece of code that itself has a large number of external dependencies. Rather than import the large code, it may be easier to bypass it altogether. This is a good technique when you have a piece of code that&#x2019;s indepedent from the rest of the code, but still is included.</p> <p>In other words, whenever a sales or marketing person forwards you an email with code you &#x201C;just have to drop in&#x201D;&#x200A;&#x2014;&#x200A;a tracking pixel, a buy now widget or so on&#x200A;&#x2014;&#x200A;you may have something you need to ignore.</p> <p>Suppose you have a piece of code that adds a map of some sort. Assume the stripped down version of the code you are importing looks something like this:</p> <pre class="md-code-block"><code class="md-code md-lang-xml">const tiles = [...Array(1000)].map(() =&gt; &apos;<span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span><span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>&apos;)
const container = `
  <span class="md-code-tag">&lt;<span class="md-code-title">div</span> <span class="md-code-attribute">class</span>=<span class="md-code-value">&quot;container&quot;</span>&gt;</span>${tiles.join(&apos;&apos;)}<span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
`

export default function render() {
  const mapContainer = document.querySelector(&apos;#map&apos;)
  mapContainer.innerHTML = container
}
</code></pre> <p>Nothing too difficult here, but it does make a few assumptions about the DOM. It runs a <code class="md-code md-code-inline">querySelector</code> and expect to see the <code class="md-code md-code-inline">innerHTML</code>.</p> <p>Suppose that you have a small React component that list some ride options and also renders the map. The map is effectively an image as far as you are concerned. You just need to make sure that a <code class="md-code md-code-inline">div</code> exists so you can add the map to it at runtime.</p> <pre class="md-code-block"><code class="md-code md-lang-xml">import React, { useEffect } from &apos;react&apos;
import renderMap from &apos;streetMap&apos;

export default function RideFinder({ rides }) {
  useEffect(() =&gt; {
    renderMap()
  }, [])
  return (
    <span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">h3</span>&gt;</span>Find a Ride<span class="md-code-tag">&lt;/<span class="md-code-title">h3</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ul</span>&gt;</span>
        {rides.map(ride =&gt; <span class="md-code-tag">&lt;<span class="md-code-title">li</span> <span class="md-code-attribute">key</span>=<span class="md-code-value">{ride.id}</span>&gt;</span>{ride.name}<span class="md-code-tag">&lt;/<span class="md-code-title">li</span>&gt;</span>)}
      <span class="md-code-tag">&lt;/<span class="md-code-title">ul</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">div</span> <span class="md-code-attribute">id</span>=<span class="md-code-value">&quot;map&quot;</span>&gt;</span><span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</code></pre> <p>In your test, you don&#x2019;t care about the map. It&#x2019;s a blackbox, but you do want to make sure you are creating the correct number of list items.</p> <pre class="md-code-block"><code class="md-code md-lang-xml">import React from &apos;react&apos;
import { mount } from &apos;enzyme&apos;
import RideFinder from &apos;./RideFinder&apos;

function flushPromises() {
  return new Promise(resolve =&gt; setTimeout(resolve, 10))
}

describe(&apos;RideFinder&apos;, () =&gt; {
  const rides = [
    {
      name: &apos;River Trail&apos;,
      id: 1,
    },
    {
      name: &apos;Downtown Bikepath&apos;,
      id: 2,
    }
  ]

  it(&apos;should render ride finder&apos;, async () =&gt; {
    const wrapper = mount(<span class="md-code-tag">&lt;<span class="md-code-title">RideFinder</span> <span class="md-code-attribute">rides</span>=<span class="md-code-value">{rides}</span>/&gt;</span>)
    const renderedRides = wrapper.find(&apos;li&apos;)
    await flushPromises()
    expect(renderedRides.length).toBe(2)
  })
})
</code></pre> <p>Note there are a few complications. To make sure <code class="md-code md-code-inline">useEffect</code> runs in enzyme, you need a small timeout to ensure everything finishes rendering. There are <a href="https://www.github.com/kentcdodds/react-testing-library" target="_blank" rel="noopener noreferrer" aria-label="react-testing-library on GitHub">other testing options than <code class="md-code md-code-inline">enzyme</code></a>, but <code class="md-code md-code-inline">enzyme</code> remains popular.</p> <p>After running this code, you get an error:</p> <p><code class="md-code md-code-inline">Uncaught TypeError: Cannot set property &apos;innerHTML&apos; of null</code></p> <p>The DOM manipulations are causing problems. In this case, you may want to mock out the map code.</p> <p>As with <code class="md-code md-code-inline">lodash</code> above, make a file of the same name as the module in the <code class="md-code md-code-inline">__mocks__</code> directory. In this case, you are using the main default, to you don&#x2019;t need an additional directory. Together, it would look like this:</p> <pre class="md-code-block"><code class="md-code">| __mocks__
| + lodash
|   + uniqueId.js
| + streetMap.js
| node_modules
</code></pre> <p>Inside of <code class="md-code md-code-inline">streetMap.js</code> simply return a mock function. Remember, in this case, you don&#x2019;t really care what it does. You just want to make sure it runs.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-keyword">default</span> jest.fn()
</code></pre> <p>At this point, your test will run with no problems. However, if you want to ensure that the your code will run the blackbox, even though you don&#x2019;t care about the output, you can still assert that it works. As with Lodash, import the code you want to check, then add an expect function to see if it is called:</p> <pre class="md-code-block"><code class="md-code md-lang-xml">import React from &apos;react&apos;
import { mount } from &apos;enzyme&apos;
import RideFinder from &apos;./RideFinder&apos;
import renderMap from &apos;streetMap&apos;

function flushPromises() {
  return new Promise(resolve =&gt; setTimeout(resolve, 10))
}

describe(&apos;RideFinder&apos;, () =&gt; {
  const rides = [
    {
      name: &apos;River Trail&apos;,
      id: 1,
    },
    {
      name: &apos;Downtown Bikepath&apos;,
      id: 2,
    }
  ]

  it(&apos;should render ride finder&apos;, async () =&gt; {
    const wrapper = mount(<span class="md-code-tag">&lt;<span class="md-code-title">RideFinder</span> <span class="md-code-attribute">rides</span>=<span class="md-code-value">{rides}</span>/&gt;</span>)
    await flushPromises()
    expect(renderMap).toBeCalled()
  })
})
</code></pre> <p>You now have a test that ensures that blackbox code is called while preventing the code from running itself. Since you have no control over the code, and it is fairly independent, this is a good strategy to ensure coverage without overcomplicating your test suite.</p> <h3 id="using-mocks-responsibly">Using mocks responsibly</h3> <p>Mocks give you a lot of power to bypass code. However, that power can also be dangerous. As soon as you bypass code, you are creating tests that do not fully execute your code. If you make false assumptions in your mocks, errors will begin to sneak into your code. And I can tell you from experience, this can happen quickly and tracking it down can be frustrating.</p> <p>As a rule, only use mocks in situations where the side effect is <em>very</em> isolated. As you can see with the Lodash functions, you don&#x2019;t want to mock everything, just the piece that causes problems. If you use other Lodash methods, you want to ensure they run properly.</p> <p>Same with the blackbox code. Nothing in the rest of your code is dependent on the results. So you can safely mock it out without creating too many additional assumptions in your tests.</p> <p>Mocks can be a powerful tool that can get you more code coverage with a simple interface. Use them with caution, but use them confidently when you do.</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/disguise-driven-testing-jest-mocks-in-depth-part-2</link><guid isPermaLink="true">https://ponyfoo.com/articles/disguise-driven-testing-jest-mocks-in-depth-part-2</guid><category><![CDATA[jest]]></category><category><![CDATA[testing]]></category><dc:creator><![CDATA[jsmapr1@gmail.com (Joe Morgan)]]></dc:creator><pubDate>Mon, 22 Apr 2019 09:26:23 GMT</pubDate></item><item><title><![CDATA[Conflict Resolution and Code Reviews]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Have you ever tried to do a code review on a PR that merges a large release branch or feature branch back into mainline, fixing merge conflicts? It&#x2019;s not pretty. The diffs are often and easily very big, <em>&#x2014; 50k+ LOC big &#x2014;</em> have hundreds of commits, and the actual changes made by the engineer resolving the merge conflicts are virtually impossible to spot.</p><p>This article covers one nifty trick I&#x2019;ve been using lately in these cases that lets you review exactly the conflict resolution, while ignoring the mountains of changes that don&#x2019;t represent any conflicts (and can thus be safely ignored).</p><h1 id="check-out-the-pr-locally">Check out the PR locally</h1> <p>If you haven&#x2019;t done this before, you can just add the author&#x2019;s remote (provided they&#x2019;re using a fork, otherwise you can skip this step) like so:</p> <pre class="md-code-block"><code class="md-code md-lang-bash">$ git remote add nico https://github.com/nico/repo.git
</code></pre> <p>Then we can fetch the PR branch, let&#x2019;s assume it&#x2019;s called <code class="md-code md-code-inline">fix-merge-conflicts</code> and it was merging <code class="md-code md-code-inline">branch-with-conflicts</code> into <code class="md-code md-code-inline">master</code>, fixing the conflicts along the way:</p> <pre class="md-code-block"><code class="md-code md-lang-bash">$ git fetch nico fix-merge-conflicts
$ git checkout fix-merge-conflicts
</code></pre> <h1 id="assess-the-situation">Assess the situation</h1> <p>The <code class="md-code md-code-inline">git log</code> should indicate they&#x2019;ve merged a couple branches. The commit hashes are the key bits here.</p> <pre class="md-code-block"><code class="md-code md-lang-bash">$ git <span class="md-code-built_in">log</span> -<span class="md-code-number">1</span> HEAD
commit a9c9ed7gcc0a76670d86df4b732f1f219ccb48de (HEAD -&gt; fix-merge-conflicts)
Merge: <mark class="md-mark md-code-mark">aecf730802</mark> <mark class="md-mark md-code-mark">ba9918dad5</mark>
Author: Bloodninja &lt;bloodninja@usenet.org&gt;
Date:   Mon Apr <span class="md-code-number">8</span> <span class="md-code-number">12</span>:<span class="md-code-number">32</span>:<span class="md-code-number">38</span> <span class="md-code-number">2019</span> +<span class="md-code-number">0000</span>

    Merge remote-tracking branch <span class="md-code-string">&apos;branch-with-conflicts&apos;</span>
</code></pre> <p>And now we can verify that these indeed belong to each of the branches we care about:</p> <pre class="md-code-block"><code class="md-code md-lang-bash">$ git branch --contains=<mark class="md-mark md-code-mark">aecf730802</mark>
  master
* fix-merge-conflicts
$ git branch --contains=<mark class="md-mark md-code-mark">ba9918dad5</mark>
  branch-with-conflicts
* fix-merge-conflicts
</code></pre> <h2 id="now-for-the-magic">Now for the magic</h2> <pre class="md-code-block"><code class="md-code md-lang-bash">git checkout -b what-were-they-thinking
git reset --hard <mark class="md-mark md-code-mark">aecf730802</mark>
git merge --no-ff <mark class="md-mark md-code-mark">ba9918dad5</mark> <span class="md-code-comment"># this will conflict</span>
git add .
git commit -m WIP
</code></pre> <p>At this point, <code class="md-code md-code-inline">what-were-they-thinking</code> is the version of the conflict resolution where you just committed things exactly as <code class="md-code md-code-inline">git merge</code> left them, conflict markers and all.</p> <p>And, presto, the following command lets you see exactly what they did.</p> <pre class="md-code-block"><code class="md-code md-lang-bash">git diff what-were-they-thinking fix-merge-conflicts
</code></pre> <p>Obviously all the commit hashes, branch names, and the author are made up here, and you&#x2019;ll have to use the actual output you get in each command to feed that into the following command, but you already knew that so I&#x2019;ll just stop mansplaining and typing these words now!</p> <p>Do you have any other quick and dirty tricks like this that would blow most people&#x2019;s minds? This one certainly blew my mind, although I can&#x2019;t claim credit for it. All credit goes to my brilliant co-worker <a href="https://twitter.com/CrazyJoeGallo" target="_blank" rel="noopener noreferrer" aria-label="@CrazyJoeGallo on Twitter">Joe Gallo</a>, who taught me this nifty trick and is a <code class="md-code md-code-inline">git</code> mastermind! </p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/conflict-resolution-and-code-reviews</link><guid isPermaLink="true">https://ponyfoo.com/articles/conflict-resolution-and-code-reviews</guid><category><![CDATA[git]]></category><category><![CDATA[tips]]></category><dc:creator><![CDATA[nico@ponyfoo.com (Nicolás Bevacqua)]]></dc:creator><pubDate>Wed, 10 Apr 2019 10:00:46 GMT</pubDate></item><item><title><![CDATA[Disguise-Driven Testing: Jest Mocks in Depth]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Testing <em>can</em> be simple. In fact, it is simple. Well, it <em>is</em> simple until impurities slip in.</p> <p>Code that would be easy to test becomes a nightmare as soon as you get impure data (like date checks) or complex external dependencies (such as DOM manipulations or large 3rd party libraries). The part that tends to frustrate developers most is when they have code that&#x2019;s easy to read, easy to write, but difficult to test. No one wants to spend more time on their tests than they do on their code.</p><p>Consider an example. Let&#x2019;s say you needed to write some code that checks to see if a given date is two weeks from today. If they date is far enough away, you get an empty string. If the date is less than two weeks away, you get an error message:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import moment from <span class="md-code-string">&apos;moment&apos;</span>;

export <span class="md-code-keyword">const</span> message = <span class="md-code-string">&apos;Date must be two weeks from now&apos;</span>

export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">isAtLeastTwoWeeks</span><span class="md-code-params">(date)</span> </span>{
  <span class="md-code-keyword">const</span> isLater = moment(date).isAfter(moment().add(<span class="md-code-number">2</span>, <span class="md-code-string">&apos;weeks&apos;</span>));
  <span class="md-code-keyword">return</span> isLater ? <span class="md-code-string">&apos;&apos;</span> : message;
}
</code></pre> <p>That&#x2019;s pretty simple code. But how do you write a test for it? Things can get tricky fast.</p><p>Here&#x2019;s an even more basic example. You want to fetch some data from an API and display the first couple items:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">getRecent</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> fetch(<span class="md-code-string">&apos;/albums&apos;</span>)
    .then(response =&gt; response.json())
    .then(albums =&gt; {
      <span class="md-code-keyword">return</span> albums
        .reverse()
        .slice(<span class="md-code-number">0</span>, <span class="md-code-number">2</span>)
        .map(({title}) =&gt; title);
    })
}
</code></pre> <p>Simple to read. Simple to understand. But how would you test it without hitting the live API?</p> <p>There&#x2019;s an unfortunate belief that testing is hard. Early in my career, I heard one developer estimate that it doubled development time. And he was arguing <em>for</em> testing, that it was worth the extra time. The problem is that most testing is easy. But testing impure functions can be very, very difficult.</p> <p>Fortunately, the <a href="https://medium.com/r/?url=https%3A%2F%2Fjestjs.io" target="_blank" rel="noopener noreferrer">Jest testing framework</a> has simplified a lot of the complexities behind testing. The secret is a tool called a <a href="https://medium.com/r/?url=https%3A%2F%2Fjestjs.io%2Fdocs%2Fen%2Fmanual-mocks" target="_blank" rel="noopener noreferrer">mock</a>. A mock is effectively a shortened version of a function. Instead of running the function you stub out the result you want. Your code will return what you want instead of running the live code.</p> <p>In other words, you say what an API <em>should</em> return. The mock will bypass the live API and return the data directly. Instead of relying on a date function that changes every 24 hours, you effectively freeze it in place by returning the same date every time.</p> <blockquote> <h5 id="warning"> Warning </h5> <p>Mocks are a great tool, but they should be a last resort. If your tests use a significant amount of mocks (or spies or stubs or other testing trick), consider refactoring first. After you have isolated the complexities as much as possible, then it&#x2019;s time to use mocks.</p> </blockquote> <h3 id="jest-a-brief-intro">Jest: A Brief Intro</h3> <p>Jest is a testing framework developed at facebook, it&#x2019;s used most often with React, but it does work independently.</p> <p>Jest offers runtime testing tools. an expect library and system for mocks and spies right out of the box.</p> <p>At it&#x2019;s most basic, a Jest test will look nearly identical to mocha or jasmine or any other framework. In fact, Jest started as a fork of Jasmine.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { double } from <span class="md-code-string">&apos;./double&apos;</span>;

describe(<span class="md-code-string">&apos;Some Test&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should add two number&apos;</span>, () =&gt; {
    expect(double(<span class="md-code-number">1</span>)).toEqual(<span class="md-code-number">2</span>);
  });
});
</code></pre> <p>In this case, <code class="md-code md-code-inline">toEqual</code> is the assertion.</p> <p>That&#x2019;s pretty much all you need for the purposes of this article. There are plenty of extra features that you can read about in the <a href="https://medium.com/r/?url=https%3A%2F%2Fjestjs.io" target="_blank" rel="noopener noreferrer">documentation</a>. You can also look there for installation and configuration. If you want to follow along, check out the <a href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fjsmapr1%2Fjest-mock-dive" target="_blank" rel="noopener noreferrer">repo for this article</a>.</p> <h3 id="creating-testable-api-functions">Creating Testable API Functions</h3> <p>Return to your API code. Now that you have your code and you have your test suite all set up, it&#x2019;s time to write tests. As a reminder, you have a piece of code that fetches API data and parses the results.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">getRecent</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> fetch(<span class="md-code-string">&apos;/albums&apos;</span>)
    .then(response =&gt; response.json())
    .then(albums =&gt; {
      <span class="md-code-keyword">return</span> albums
        .reverse()
        .slice(<span class="md-code-number">0</span>, <span class="md-code-number">2</span>)
        .map(({title}) =&gt; title);
    })
}
</code></pre> <p>The first step in creating a mock is isolating the pain point. In this case, the problem is the <code class="md-code md-code-inline">fetch</code> block. Specifically this part:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">return</span> fetch(<span class="md-code-string">&apos;/albums&apos;</span>)
    .then(response =&gt; response.json())
</code></pre> <p>The rest of the code isn&#x2019;t so bad. It&#x2019;s just a series of array manipulations.</p> <p>The next step if figuring out what kind of data from the hard part you expect to pass down to the easy part. Again, all you need to do is look at what the live API will return by either hitting it directly or by getting the contract from some other team. In this case, you plan to get the albums of the great doom metal band Sleep. The data you expect to get will look something like this:</p> <pre class="md-code-block"><code class="md-code md-lang-json">[
  {
    &quot;<span class="md-code-attribute">year</span>&quot;: <span class="md-code-value"><span class="md-code-number">1992</span></span>,
    &quot;<span class="md-code-attribute">title</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;Holy Mountain&quot;</span>
  </span>},
  {
    &quot;<span class="md-code-attribute">year</span>&quot;: <span class="md-code-value"><span class="md-code-number">1999</span></span>,
    &quot;<span class="md-code-attribute">title</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;Jerusalem&quot;</span>
  </span>},
  {
    &quot;<span class="md-code-attribute">year</span>&quot;: <span class="md-code-value"><span class="md-code-number">2014</span></span>,
    &quot;<span class="md-code-attribute">title</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The Clarity&quot;</span>
  </span>},
  {
    &quot;<span class="md-code-attribute">year</span>&quot;: <span class="md-code-value"><span class="md-code-number">2018</span></span>,
    &quot;<span class="md-code-attribute">title</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The Sciences&quot;</span>
  </span>}
]
</code></pre> <p>Now, you should split the function. Pull the hard part away from the easy part. That will make your tests cleaner and easier to mock.</p> <p>As a quick side note, some consider it bad practice to break up your code to make it more testable. I would disagree. Testability tells you something about the complexity of your code. The harder it is to test, the more complex it tends to be. When you are refactoring your code to make it more testable, you just changing the complex to the simple.</p> <p>Since you&#x2019;ve isolated the complex part, the next the step is pulling the <code class="md-code md-code-inline">fetch</code> call out into a separate function. Call the new function<code class="md-code md-code-inline">service</code> and since it now has a separate responsibility go ahead and move it into a new file called <code class="md-code md-code-inline">service.js</code>. To keep things clean, move it to a separate directory called <code class="md-code md-code-inline">api</code> this will be important later when you set up the mock.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">service</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> fetch(<span class="md-code-string">&apos;/albums&apos;</span>)
    .then(response =&gt; response.json())
}
</code></pre> <p>Now you need to import the service and replace the original <code class="md-code md-code-inline">fetch</code> method. Your original function will be a little smaller, but you still are able to perform the exact same actions as your did before:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import service from <span class="md-code-string">&apos;../api/service&apos;</span>;

export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">getRecent</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> service()
    .then(albums =&gt; {
      <span class="md-code-keyword">return</span> albums
        .reverse()
        .slice(<span class="md-code-number">0</span>, <span class="md-code-number">2</span>)
        .map(({ title }) =&gt; title);
  })
}
</code></pre> <p>At this point, you should start to see an opportunity for a mock. Remember, a mock is just replacing a live piece of code with a simple function that returns the data you expect.</p> <p>In this case, you have a function <code class="md-code md-code-inline">service</code> that returns a <code class="md-code md-code-inline">Promise</code> that returns some sort of array. You happen to know this service hits an API, but that doesn&#x2019;t really matter. It can be loading data from <code class="md-code md-code-inline">localstorage</code> as far as you&#x2019;re concerned.</p> <p>Time to jump into the test. Here&#x2019;s what it&#x2019;ll look like:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { getRecent } from <span class="md-code-string">&apos;./recent&apos;</span>;

describe(<span class="md-code-string">&apos;getRecent&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should get albums&apos;</span>, async () =&gt; {
    <span class="md-code-keyword">const</span> albums = await getRecent();
    <span class="md-code-keyword">const</span> expected = [
      <span class="md-code-string">&apos;The Sciences&apos;</span>,
      <span class="md-code-string">&apos;The Clarity&apos;</span>
    ]
    expect(albums).toEqual(expected);
  })
})
</code></pre> <p>Since <code class="md-code md-code-inline">getRecent</code> returns a promise, you can use <code class="md-code md-code-inline">async/await</code> syntax. This code says, run the <code class="md-code md-code-inline">getRecent</code> function and once it resolves, make sure the albums are as expected.</p> <p>Jest will wait for a promise to resolve if you return. That means you can also write the test like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { getRecent } from <span class="md-code-string">&apos;./recent&apos;</span>;

describe(<span class="md-code-string">&apos;getRecent&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should get albums&apos;</span>, () =&gt; {
    <span class="md-code-keyword">return</span> getRecent()
      .then(albums =&gt; {
        <span class="md-code-keyword">const</span> expected = [
          <span class="md-code-string">&apos;The Sciences&apos;</span>,
          <span class="md-code-string">&apos;The Clarity&apos;</span>
        ]
        expect(albums).toEqual(expected);
      })
  })
})
</code></pre> <p>Either way, you still need to account for the service. Currently, it will still try (and probably fail) to hit a live endpoint.</p> <p>Now, it&#x2019;s time to start mocking the results.</p> <h3 id="mocking-an-api-result">Mocking an API Result</h3> <p>There are multiple ways to mock data, but the easiest is by creating a manual mock at the same level as the code you need to bypass.</p> <p>In practical terms, you are going to make a file of the same name that returns data that you want instead of executing a function to get that data.</p> <p>Start by making a directory called <code class="md-code md-code-inline">__mocks__</code> in the <em>same</em> directory as <code class="md-code md-code-inline">service.js</code> Suppose you had a directory called <code class="md-code md-code-inline">api</code> that contained <code class="md-code md-code-inline">services.js</code>, you&#x2019;d add your <code class="md-code md-code-inline">__mocks__</code> in the same <code class="md-code md-code-inline">api</code> directory like this:</p> <pre class="md-code-block"><code class="md-code">| api
  | __mocks__
    | service.js
  | service.js
</code></pre> <p>Inside <code class="md-code md-code-inline">__mocks__/service.js</code> create a function with the same name, but that returns the data you want.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> albums = [
  {
    year: <span class="md-code-number">1992</span>,
    title: <span class="md-code-string">&apos;Holy Mountain&apos;</span>
  },
  {
    year: <span class="md-code-number">1999</span>,
    title: <span class="md-code-string">&apos;Jerusalem&apos;</span>
  },
  {
    year: <span class="md-code-number">2014</span>,
    title: <span class="md-code-string">&apos;The Clarity&apos;</span>
  },
  {
    year: <span class="md-code-number">2018</span>,
    title: <span class="md-code-string">&apos;The Sciences&apos;</span>
  },
];

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">service</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> Promise.resolve(albums)
}
</code></pre> <p>Just as with the actual <code class="md-code md-code-inline">service</code> function. This one returns a <code class="md-code md-code-inline">Promise</code> but unlike the original function, this one has the data hard coded. It will never change.</p> <p>Now that you&#x2019;ve set up your mock, you need to alert your test that you want to use the manual mock instead of the real function. To do that, you need to use <code class="md-code md-code-inline">jest.mock(path)</code> where the <code class="md-code md-code-inline">path</code> is the location of the file you want to mock.</p> <p>Suppose you had a file structure like this:</p> <pre class="md-code-block"><code class="md-code">| albums
  | recent.js
  | recent.spec.js
| api
  | __mocks__
    | service.js
  | service.js
</code></pre> <p>In your test, add the line <code class="md-code md-code-inline">jest.mock(&apos;../api/service&apos;)</code>. Notice, you are not adding the path to the <code class="md-code md-code-inline">__mocks__</code> directory, you are using the same path as if you were importing the function. Jest will find the mock for you. You are using the same path as you would in <code class="md-code md-code-inline">recent.js</code>.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import { getRecent } from <span class="md-code-string">&apos;./recent&apos;</span>;

jest.mock(<span class="md-code-string">&apos;../api/service&apos;</span>);

describe(<span class="md-code-string">&apos;recent&apos;</span>, () =&gt; {
  it(<span class="md-code-string">&apos;should get albums&apos;</span>, async () =&gt; {
    <span class="md-code-keyword">const</span> albums = await getRecent();
    <span class="md-code-keyword">const</span> expected = [
      <span class="md-code-string">&apos;The Sciences&apos;</span>,
      <span class="md-code-string">&apos;The Clarity&apos;</span>
    ]
    expect(albums).toEqual(expected);
  })
})
</code></pre> <p>Now, you are not hitting a live API. You are hitting an internal simulation. And it only takes one line of code. You don&#x2019;t need network connectivity. You don&#x2019;t need to worry about results changing. You don&#x2019;t need to spin up a separate service. Everything is happening internally.</p> <h3 id="what-s-next">What&#x2019;s next?</h3> <p>Mocking API data is one of the best uses of mocks. Nearly all JavaScript projects need to be able to access remote data. However, mocks can do a lot more. They can also deal with complicated side effects such as date time or DOM manipulations. In my next article, I&#x2019;ll show you how to mock out <code class="md-code md-code-inline">moment</code> and other libraries that return data that changes depending on the day or even the location of the test.</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/disguise-driven-testing-jest-mocks-in-depth</link><guid isPermaLink="true">https://ponyfoo.com/articles/disguise-driven-testing-jest-mocks-in-depth</guid><category><![CDATA[testing]]></category><category><![CDATA[jest]]></category><dc:creator><![CDATA[jsmapr1@gmail.com (Joe Morgan)]]></dc:creator><pubDate>Thu, 28 Mar 2019 03:18:25 GMT</pubDate></item><item><title><![CDATA[The Action Pattern: Clean, Obvious, Testable Code]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Let&#x2019;s convert a mock API endpoint for signing up new users in a mobile app into using the action pattern.</p><p>When I first started writing software on the web, my code was a mishmash. Every project was loaded with unnecessarily long files and code left commented, thrown to the side of the road like an abandoned vehicle. The theme of the day was: unpredictability.</p> <p>Under ideal conditions&#x2014;the happy path&#x2014;I was able to make my code work. But what I couldn&#x2019;t do was make my code work <em>consistently</em>. One time through my code would work, then the next an anonymous <code class="md-code md-code-inline">500 Internal Server Error</code> would send me spiraling for days.</p> <p>I was able to squeak by, but the thought of having to continue answering emails from clients that read &#x201C;this isn&#x2019;t working&#x2026;&#x201D; was a life I didn&#x2019;t want to lead.</p> <p>Taking off my beginner&#x2019;s hat, I started to see what other, more experienced programmers were up to. I&#x2019;d heard about Bob &#x201C;Uncle Bob&#x201D; Martin in passing, eventually discovering <a href="https://cleancoders.com/videos/clean-code/fundamentals" target="_blank" rel="noopener noreferrer">his Clean Coders series</a>. I was hooked. For the first time, he was answering questions others on my path hadn&#x2019;t.</p><p>My primary question? &#x201C;How do I organize complex code?&#x201D; As far as questions go that was a ball of yarn, but over several videos he explained the parts I was missing:</p> <ul> <li>Using explicit names that can&#x2019;t be mistaken.</li> <li>Breaking your code into functions that do one thing.</li> <li>Using TDD (test-driven development) to guide yout work.</li> </ul> <p>Still green, some of this made sense and some of it didn&#x2019;t. The other problem was that Bob&#x2019;s language of choice was Java, not <em>JavaScript</em>. This meant that I was able to grasp what he was saying at a high level, but practically I was still stumped.</p> <h3 id="several-iterations-later">Several iterations later&#x2026;</h3> <p>Eventually, what Bob taught started to sink in. As I gained experience, I slowly started to organize my code into a pattern (supported by a short list of rules):</p> <ol> <li>Any code that involves multiple steps should be moved into its own file/module.</li> <li>That file/module should be given a name that describes what those steps lead up to.</li> <li>Each step in that code should be a single function with a name that describes exactly what it does (even if it&#x2019;s longer than we prefer).</li> <li>If the code fails, it should be easy to see exactly <em>where</em> it failed without a lot of backstepping.</li> </ol> <p>What started out as an informal set of rules for myself eventually evolved into a concrete pattern. After years of iteration and putting it through the paces on client and personal projects, in 2017 the action pattern was christened.</p> <h3 id="how-actions-work">How Actions work</h3> <p>For the remainder of this tutorial, we&#x2019;re going to convert a mock API endpoint for signing up new users in a mobile app into an action. Our goals:</p> <ol> <li>Understand the structure of an action.</li> <li>Learn how to use JavaScript Promises with actions.</li> <li>Find a greater &#x201C;why&#x201D; for using actions.</li> <li>Understand how writing tests is simplified by using actions.</li> </ol> <h2 id="converting-our-existing-api-endpoint">Converting Our Existing API Endpoint</h2> <p>Our app, Doodler (a paid social network for artists), handles its signups via an existing Express-based API. When a new user signs up in the app, a request is made to their API at <code class="md-code md-code-inline">https://doodler.fake/api/v1/users/signup</code>.</p> <p>At that endpoint, the following steps take place:</p> <ul> <li>A new user is created in the users collection.</li> <li>A new customer is created on Stripe.</li> <li>A customer is created in the customers collection.</li> <li>A welcome email is generated.</li> <li>A &#x201C;new user&#x201D; message is sent to the company&#x2019;s Slack.</li> </ul> <p>Together, these five steps represent the <em>action</em> of signing up a new user. Because some of the steps are dependent on prior steps, we want to have some way to &#x201C;stop&#x201D; our code if earlier steps fail. Before we get into the weeds, let&#x2019;s take a look at the code we have now:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">/* eslint-disable */</span>

import mongodb from <span class="md-code-string">&apos;/path/to/mongodb&apos;</span>;
import settings from <span class="md-code-string">&apos;/path/to/settings&apos;</span>;
import stripe from <span class="md-code-string">&apos;/path/to/stripe/api&apos;</span>;
import imaginaryEmailService from <span class="md-code-string">&apos;/path/to/imaginaryEmailService&apos;</span>;
import slackLog from <span class="md-code-string">&apos;/path/to/slackLog&apos;</span>;

export <span class="md-code-keyword">default</span> {
  v1: {
    <span class="md-code-string">&apos;/users/signup&apos;</span>: (request, response) =&gt; {
      mongodb.connect(settings.mongodb.url, <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-params">(error, client)</span> </span>{
        <span class="md-code-keyword">const</span> db = client.db(<span class="md-code-string">&apos;production&apos;</span>);
        <span class="md-code-keyword">const</span> users = db.collection(<span class="md-code-string">&apos;users&apos;</span>);
        <span class="md-code-keyword">const</span> customers = db.collection(<span class="md-code-string">&apos;customers&apos;</span>);

        users.insert({ email: request.body.email, password: request.body.password, profile: request.body.profile }, async <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-params">(error, insertedUser)</span> </span>{
          <span class="md-code-keyword">if</span> (error) {
            <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(error);
          } <span class="md-code-keyword">else</span> {
            <span class="md-code-keyword">const</span> [user] = insertedUser;
            <span class="md-code-keyword">const</span> userId = user._id;
  
            <span class="md-code-keyword">const</span> customerOnStripe = await stripe.customers.create({
              email: request.body.email,
            });

            customers.insert({ userId, stripeCustomerId: customerOnStripe.id }, async <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-params">(error, insertedCustomer)</span> </span>{
              <span class="md-code-keyword">if</span> (error) {
                <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(error);
              } <span class="md-code-keyword">else</span> {
                imaginaryEmailService.send({ to: request.body.email, template: <span class="md-code-string">&apos;welcome&apos;</span> });
                slackLog.success({
                  message: <span class="md-code-string">&apos;New Customer&apos;</span>,
                  metadata: {
                    emailAddress: request.body.email,
                  },
                });

                response.end();
              }
            });
          }
        });
      });
    },  
  },
};
</code></pre> <p>Looking at this code, assuming that all of the parts in use work on their own, it&#x2019;s plausible that this code will work. What&#x2019;s distinct about this code, however, is that it&#x2019;s not terribly organized. It contains a lot of nested calls and not much flow control (i.e., if something fails, the whole house of cards falls).</p> <p>This is where we start to tiptoe up to the &#x201C;works&#x201D; vs. &#x201C;works well&#x201D; chasm. Unfortunately, it&#x2019;s code like this that leads to a lot of wasted time chasing down and fixing bugs. It&#x2019;s not that the code doesn&#x2019;t work, it&#x2019;s that it works <em>unpredictably</em>.</p> <p>You&#x2019;re probably saying &#x201C;well yeah, <em>all</em> code is unpredictable.&#x201D; You&#x2019;re not wrong. But, if we&#x2019;re smart we can significantly reduce the amount of unpredictability, giving us more time to focus on fun stuff&#x2014;not fixing mistakes of the past (either of our own making or someone on our team).</p> <h2 id="introducing-the-action-pattern">Introducing the Action Pattern</h2> <p>First and foremost, it&#x2019;s important to understand that the action pattern is vanilla JavaScript. It&#x2019;s a <em>pattern</em> to follow, not a library or framework to implement. This means that using actions requires a certain level of discipline (the majority of which can be automated via snippets in your IDE).</p> <p>To get started with our conversion, let&#x2019;s look at a skeleton version of an action and then build it up to handle our new user signup.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">/* eslint-disable consistent-return */</span>

<span class="md-code-keyword">const</span> actionMethod = (someOption) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-built_in">console</span>.log(<span class="md-code-string">&apos;Do something with someOption&apos;</span>, someOption);
    <span class="md-code-comment">// Perform a single step in your action here.</span>
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[actionName.actionMethod] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">if</span> (!options) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options object is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.someOption) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.someOption is required.&apos;</span>);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[actionName.validateOptions] ${exception.message}`);
  }
};

export <span class="md-code-keyword">default</span> (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);
    actionMethod(options.someOption);
    <span class="md-code-comment">// Call action methods in sequence here.</span>
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[actionName] ${exception.message}`);
  }
};
</code></pre> <p>Actions are designed to be read from the bottom up. At the bottom of our file, we export a function known as our handler. This function is responsible for calling to all of the other steps in our action. This helps us to accomplish a few things:</p> <ol> <li>Centralize all of our calls to other code in one place.</li> <li>Share response values from each step with other steps.</li> <li>Clearly delineate the order of steps in our code.</li> <li>Make our code more maintainable and extensible by avoiding nested spaghetti code.</li> </ol> <p>Inside of this function, the very first thing we do is call to <code class="md-code md-code-inline">validateOptions</code> passing in the assumed <code class="md-code md-code-inline">options</code> argument passed to the handler function (or, what we export from our file as our action).</p> <p>With <code class="md-code md-code-inline">validateOptions</code> we start to see a few other sub-patterns of actions appear. Specifically, the name of the <code class="md-code md-code-inline">validateOptions</code> function is called <em>exactly what it does</em>. It&#x2019;s not <code class="md-code md-code-inline">vldOpts</code> or <code class="md-code md-code-inline">validateOps</code> or anything that leaves room for confusion. If I were to drop another developer into this code, and ask them &#x201C;what does that function do?&#x201D; they&#x2019;ll most likely respond sarcastically with &#x201C;uhh, validates the options?&#x201D;</p> <p>The next thing you&#x2019;ll notice is the structure of <code class="md-code md-code-inline">validateOptions</code>. Immediately inside of the function body, a <code class="md-code md-code-inline">try/catch</code> statement is added, with the <code class="md-code md-code-inline">catch</code> portion taking the <code class="md-code md-code-inline">exception</code> and <code class="md-code md-code-inline">throw</code>ing it using the JavaScript <code class="md-code md-code-inline">Error</code> constructor. Notice, too, that <strong>when this error is thrown, we tell ourselves <em>exactly where the error is happening</em></strong> with <code class="md-code md-code-inline">[actionName.validateOptions]</code> followed by the specific error message.</p> <p>In the <code class="md-code md-code-inline">try</code> block, we do what our code says: validate our <code class="md-code md-code-inline">options</code>! The logic here is kept simple on purpose. If our action requires that <code class="md-code md-code-inline">options</code> be passed and requires specific properties to be defined in those <code class="md-code md-code-inline">options</code>, we throw an error if they don&#x2019;t exist. To make sure this clear, if we were to call this action now like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">actionName();
</code></pre> <p>We&#x2019;d get the following error in response:</p> <pre class="md-code-block"><code class="md-code md-lang-bash">[actionName.validateOptions] options object is required.
</code></pre> <p>This is a <em>serious</em> boon on development. We&#x2019;re telling ourselves exactly what we need up front so we can skip the &#x201C;what did I forget to pass now?&#x201D; roulette.</p> <p>If we move back down to our handler function, we&#x2019;ll see that after our options have been validated with <code class="md-code md-code-inline">validateOptions</code>, our next step is to call <code class="md-code md-code-inline">actionMethod</code>, passing <code class="md-code md-code-inline">options.someOptions</code>.</p> <p>This is where we get into the actual steps or functionality of our action. Here, <code class="md-code md-code-inline">actionMethod</code> takes in <code class="md-code md-code-inline">options.someOption</code>. Notice that because it&#x2019;s the second step called in our handler, it&#x2019;s defined <em>above</em> <code class="md-code md-code-inline">validateOptions</code> (our first step).</p> <p>If we look at <code class="md-code md-code-inline">actionMethod</code> things should&#x2014;purposefully&#x2014;look pretty familiar. Here, we repeat the same pattern: give a clear name for our function, run our code in a <code class="md-code md-code-inline">try/catch</code> block, and if our code fails, <code class="md-code md-code-inline">throw</code> an error telling ourselves that it came from <code class="md-code md-code-inline">[actionName.actionMethod]</code>.</p> <h3 id="refactoring-our-signup">Refactoring our signup</h3> <p>Feeling undewherlmed? Great! That&#x2019;s what we&#x2019;re after. Writing clean code shouldn&#x2019;t be difficult or excessively esoteric. Now, let&#x2019;s start to refactor our signup endpoint into an action. Let&#x2019;s clean up our skeleton, adding some legitimate checks to <code class="md-code md-code-inline">validateOptions</code>:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> actionMethod = (someOption) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-built_in">console</span>.log(<span class="md-code-string">&apos;Do something with someOption&apos;</span>, someOption);
    <span class="md-code-comment">// Perform a single step in your action here.</span>
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.actionMethod] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">if</span> (!options) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options object is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.email) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.email is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.password) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.password is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.profile) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.profile is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.response) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.response is required.&apos;</span>);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.validateOptions] ${exception.message}`);
  }
};

export <span class="md-code-keyword">default</span> (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);
    <span class="md-code-comment">// Call action methods in sequence here.</span>
    options.response.end();
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup] ${exception.message}`);
  }
};
</code></pre> <p>A few things have changed. Notice that instead of <code class="md-code md-code-inline">actionName</code>, we&#x2019;ve now given our action a name: <code class="md-code md-code-inline">signup</code>.</p> <p>Inside of <code class="md-code md-code-inline">validateOptions</code>, we&#x2019;ve set some real expectations, too. Remember that in our original code, we reuse the <code class="md-code md-code-inline">request.body</code> object several times. Here, we think ahead and make the assumption that we&#x2019;ll just pass the <code class="md-code md-code-inline">body</code> part of the request (the only part we utilize). We also make sure to validate that each of the properties <em>of</em> the body are present.</p> <p>Finally, we also want to validate that the <code class="md-code md-code-inline">response</code> object from our endpoint is passed so we can respond to the request within our action.</p> <p>The details of this are mostly arbitrary; the point here is that we&#x2019;re ensuring we have what we need <em>before we put it to use</em>. This helps to eliminate the inevitable &#x201C;did I pass that yet?&#x201D; question as well as subsequent time wasted debugging to figure it out.</p> <h3 id="adding-additional-steps-as-functions">Adding additional steps as functions</h3> <p>Now that we have our handler function set up as well as our <code class="md-code md-code-inline">validateOptions</code>, we can start to port over the core functionality for our action.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">/* eslint-disable consistent-return */</span>

import mongodb from <span class="md-code-string">&apos;/path/to/mongodb&apos;</span>;
import settings from <span class="md-code-string">&apos;/path/to/settings&apos;</span>;

<span class="md-code-keyword">const</span> connectToMongoDB = () =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      mongodb.connect(
        settings.mongodb.url,
        (error, client) =&gt; {
          <span class="md-code-keyword">if</span> (error) {
            reject(error);
          } <span class="md-code-keyword">else</span> {
            <span class="md-code-keyword">const</span> db = client.db(<span class="md-code-string">&apos;production&apos;</span>);
            resolve({
              db,
              users: db.collection(<span class="md-code-string">&apos;users&apos;</span>),
              customers: db.collection(<span class="md-code-string">&apos;customers&apos;</span>),
            });
          }
        },
      );
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.connectToMongoDB] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; [...];

export <span class="md-code-keyword">default</span> async (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);
    <span class="md-code-keyword">const</span> db = await connectToMongoDB();
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup] ${exception.message}`);
  }
};
</code></pre> <p>First, we need to establish a connection to our database. Recall, we need access to the <code class="md-code md-code-inline">users</code> and <code class="md-code md-code-inline">customers</code> collection from MongoDB. Knowing this, we can streamline our code by creating an action method <code class="md-code md-code-inline">connectToMongoDB</code> whose sole job is connecting us to MongoDB, giving us access to the databases we&#x2019;ll need to do our work.</p> <p>To do it, we wrap our call to <code class="md-code md-code-inline">mongodb.connect</code> using the action method pattern. By wrapping this code with a JavaScript <code class="md-code md-code-inline">Promise</code>, we can ensure our connection is complete <em>before</em> we try to use it. This is necessary because we&#x2019;re no longer running our subsequent code accessing the database inside of <code class="md-code md-code-inline">mongodb.connect</code>&apos;s callback. Instead, we <code class="md-code md-code-inline">resolve</code> our <code class="md-code md-code-inline">Promise</code> passing the <code class="md-code md-code-inline">db</code> connection along with the two databases that we&#x2019;ll need: <code class="md-code md-code-inline">users</code> and <code class="md-code md-code-inline">customers</code>.</p> <p>Why is this important? Consider this: our connection to MongoDB could fail. If it does, we not only want to know why, but we want our code to be easily debugged. With nested spaghetti code, this is possible, but adds mental weight.</p> <p>By encapsulating our call&#x2014;and any failures&#x2014;inside of a single function, we eliminate the need to track down errors. This is especially helpful when the errors themselves are unhelpful or ambiguous (R.I.P to souls who get an <code class="md-code md-code-inline">ECONNRESET</code>). The difference between <code class="md-code md-code-inline">ERR ECONNRESET</code> and <code class="md-code md-code-inline">[signup.connectToMongoDB] ERR ECONNRESET</code> is night and day. The error may not be clear, but we&#x2019;ve told ourselves <em>exactly</em> who&#x2019;s responsible.</p> <p>Back in our handler function, we utilize the <code class="md-code md-code-inline">async/await</code> syntax to ensure that we&#x2019;ve received a response from MongoDB <em>before</em> we continue with the rest of our action (i.e., we&#x2019;ve achieved what our callback gave us without opening an Italian restaurant).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">/* eslint-disable consistent-return */</span>

import mongodb from <span class="md-code-string">&apos;/path/to/mongodb&apos;</span>;
import settings from <span class="md-code-string">&apos;/path/to/settings&apos;</span>;

<span class="md-code-keyword">const</span> createUser = (users, userToCreate) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      users.insert(userToCreate, (error, insertedUser) =&gt; {
        <span class="md-code-keyword">if</span> (error) {
          reject(error);
        } <span class="md-code-keyword">else</span> {
          <span class="md-code-keyword">const</span> [user] = insertedUser;
          resolve(user._id);
        }
      });
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.createUser] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> connectToMongoDB = () =&gt; [...];

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; [...];

export <span class="md-code-keyword">default</span> async (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);

    <span class="md-code-keyword">const</span> db = await connectToMongoDB();
    <span class="md-code-keyword">const</span> userId = await createUser(db.users, options.body);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup] ${exception.message}`);
  }
};
</code></pre> <p>Next up is creating our user. This is where the magic of actions start to show. Down in our handler function, we add our next step <code class="md-code md-code-inline">createUser</code> beneath our first step <code class="md-code md-code-inline">connectToMongoDB</code>. Notice that when we need to reference the value returned by a previous step in future steps, we give it a variable name that represents exactly what&#x2019;s being returned.</p> <p>Here, <code class="md-code md-code-inline">const db</code> suggests we get access to our database in that variable and <code class="md-code md-code-inline">const userId</code> suggests we expect a user&#x2019;s <code class="md-code md-code-inline">_id</code> back from <code class="md-code md-code-inline">createUser</code>. In order to get there, we know that we need to connect to the <code class="md-code md-code-inline">users</code> collection in MongoDB and we need the user information passed in the <code class="md-code md-code-inline">request.body</code> to create that user. To do it, we just pass those values as arguments to <code class="md-code md-code-inline">createUser</code>. Clean and tidy.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> createUser = (users, userToCreate) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      users.insert(userToCreate, (error, insertedUser) =&gt; {
        <span class="md-code-keyword">if</span> (error) {
          reject(error);
        } <span class="md-code-keyword">else</span> {
          <span class="md-code-keyword">const</span> [user] = insertedUser;
          resolve(user._id);
        }
      });
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.createUser] ${exception.message}`);
  }
};
</code></pre> <p>Focusing just on our <code class="md-code md-code-inline">createUser</code> definition, we can see that we take in that <code class="md-code md-code-inline">db.users</code> argument as <code class="md-code md-code-inline">users</code> and <code class="md-code md-code-inline">options.body</code> as <code class="md-code md-code-inline">userToCreate</code> (remember, this should be an <code class="md-code md-code-inline">Object</code> with <code class="md-code md-code-inline">email</code>, <code class="md-code md-code-inline">password,</code> and <code class="md-code md-code-inline">profile</code> as properties).</p> <p>Using the same <code class="md-code md-code-inline">Promise</code> approach, we call to <code class="md-code md-code-inline">users.insert</code> and rely on our <code class="md-code md-code-inline">resolve</code> and <code class="md-code md-code-inline">reject</code> to handle the respective error and success states of our call to <code class="md-code md-code-inline">users.insert</code>. If our insert is successful, we get the <code class="md-code md-code-inline">_id</code> of the <code class="md-code md-code-inline">insertedUser</code> and <code class="md-code md-code-inline">resolve()</code> our <code class="md-code md-code-inline">Promise</code> with it.</p> <p>Pay close attention. Because we&#x2019;re calling <code class="md-code md-code-inline">resolve(user._id)</code>, this means that back in our <code class="md-code md-code-inline">handler</code> function, our <code class="md-code md-code-inline">const userId = createUser()</code> is now &#x201C;truthful&#x201D; because once that <code class="md-code md-code-inline">Promise</code> resolves, we&#x2019;ll get the <code class="md-code md-code-inline">userId</code> in return, assigned to that variable. Sweet!</p> <h3 id="completing-our-action">Completing our action</h3> <p>At this point, we&#x2019;re familiar with the core concepts of an action. Once the full conversion is complete, here&#x2019;s what we get:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import mongodb from <span class="md-code-string">&apos;/path/to/mongodb&apos;</span>;
import settings from <span class="md-code-string">&apos;/path/to/settings&apos;</span>;
import stripe from <span class="md-code-string">&apos;/path/to/stripe/api&apos;</span>;
import imaginaryEmailService from <span class="md-code-string">&apos;/path/to/imaginaryEmailService&apos;</span>;
import slackLog from <span class="md-code-string">&apos;/path/to/slackLog&apos;</span>;

<span class="md-code-keyword">const</span> logCustomerOnSlack = (emailAddress) =&gt; {
  <span class="md-code-keyword">try</span> {
    slackLog.success({
      message: <span class="md-code-string">&apos;New Customer&apos;</span>,
      metadata: {
        emailAddress,
      },
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.logCustomerOnSlack] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> sendWelcomeEmail = (to) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> imaginaryEmailService.send({ to, template: <span class="md-code-string">&apos;welcome&apos;</span> });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.sendWelcomeEmail] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> createCustomer = (customers, userId, stripeCustomerId) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      customers.insert({ userId, stripeCustomerId }, (error, insertedCustomer) =&gt; {
        <span class="md-code-keyword">if</span> (error) {
          reject(error);
        } <span class="md-code-keyword">else</span> {
          <span class="md-code-keyword">const</span> [customer] = insertedCustomer;
          resolve(customer._id);
        }
      });
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.createCustomer] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> createCustomerOnStripe = (email) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> stripe.customer.create({ email });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.createCustomerOnStripe] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> createUser = (users, userToCreate) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      users.insert(userToCreate, (error, insertedUser) =&gt; {
        <span class="md-code-keyword">if</span> (error) {
          reject(error);
        } <span class="md-code-keyword">else</span> {
          <span class="md-code-keyword">const</span> [user] = insertedUser;
          resolve(user._id);
        }
      });
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.createUser] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> connectToMongoDB = () =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> <span class="md-code-keyword">new</span> Promise((resolve, reject) =&gt; {
      mongodb.connect(
        settings.mongodb.url,
        (error, client) =&gt; {
          <span class="md-code-keyword">if</span> (error) {
            reject(error);
          } <span class="md-code-keyword">else</span> {
            <span class="md-code-keyword">const</span> db = client.db(<span class="md-code-string">&apos;production&apos;</span>);
            resolve({
              db,
              users: db.collection(<span class="md-code-string">&apos;users&apos;</span>),
              customers: db.collection(<span class="md-code-string">&apos;customers&apos;</span>),
            });
          }
        },
      );
    });
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.connectToMongoDB] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">if</span> (!options) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options object is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.email) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.email is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.password) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.password is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.body.profile) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.body.profile is required.&apos;</span>);
    <span class="md-code-keyword">if</span> (!options.response) <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(<span class="md-code-string">&apos;options.response is required.&apos;</span>);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.validateOptions] ${exception.message}`);
  }
};

export <span class="md-code-keyword">default</span> async (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);

    <span class="md-code-keyword">const</span> db = await connectToMongoDB();
    <span class="md-code-keyword">const</span> userId = await createUser(db.users, options.body);
    <span class="md-code-keyword">const</span> customerOnStripe = await createCustomerOnStripe(options.body.email);

    await createCustomer(db.customers, userId, customerOnStripe.id);
    sendWelcomeEmail(options.body.email);
    logCustomerOnSlack(options.body.email);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup] ${exception.message}`);
  }
};
</code></pre> <p>A few things to point out. First, all of our additional action methods have been added to our handler, called in sequence.</p> <p>Notice that after we&#x2019;ve created a customer on Stripe (and returned that customer as <code class="md-code md-code-inline">const customerOnStripe</code>), none of the steps after this need a value from the previous steps. In turn, we just call these steps independently without storing their <code class="md-code md-code-inline">return</code> value in a variable.</p> <p>Notice, too, that our <code class="md-code md-code-inline">sendWelcomeEmail</code> and <code class="md-code md-code-inline">logCustomerOnSlack</code> steps remove the usage of an <code class="md-code md-code-inline">await</code> because there&#x2019;s nothing for us to wait on.</p> <p>That&#x2019;s it! At this point, we have a complete action.</p> <h3 id="wait-but-why">Wait, but why?</h3> <p>You&#x2019;re probably wondering &#x201C;didn&#x2019;t we just add a ton of extra code to do the same thing?&#x201D; We did. But something important to consider is how much context and clarity adding that (negligible amount of) extra code gave us.</p> <p>This is the point of actions: giving us a consistent, predictable pattern for organizing complex processes. That&#x2019;s a mouthful, so another way to think about this is reducing maintenance cost. <em>Nobody</em> likes to maintain code. Often, too, when we&#x2019;re tasked with maintaining a &#x201C;legacy&#x201D; codebase, it tends to look more like the code we started with.</p> <p>What this translates to is cost. Cost in time, money, and for the people doing the work: peace of mind. When code is a tangle of pasta, there&#x2019;s a cost to <em>understanding that code</em>. The less structure and consistency, the higher that cost.</p> <p>With actions, we can significantly reduce the amount of thinking that goes into maintaining our code. Not only that, but we also make it incredibly easy to extend our code. For example, if we&#x2019;re asked to add the ability to log the new user in our analytics system, there&#x2019;s little to no thought involved.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">[...]
import analytics from <span class="md-code-string">&apos;/path/to/analytics&apos;</span>;

<span class="md-code-keyword">const</span> trackEventInAnalytics = (userId) =&gt; {
  <span class="md-code-keyword">try</span> {
    <span class="md-code-keyword">return</span> analytics.send(userId);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup.trackEventInAnalytics] ${exception.message}`);
  }
};

<span class="md-code-keyword">const</span> logCustomerOnSlack = (emailAddress) =&gt; [...];

<span class="md-code-keyword">const</span> sendWelcomeEmail = (to) =&gt; [...];

<span class="md-code-keyword">const</span> createCustomer = (customers, userId, stripeCustomerId) =&gt; [...];

<span class="md-code-keyword">const</span> createCustomerOnStripe = (email) =&gt; [...];

<span class="md-code-keyword">const</span> createUser = (users, userToCreate) =&gt; [...];

<span class="md-code-keyword">const</span> connectToMongoDB = () =&gt; [...];

<span class="md-code-keyword">const</span> validateOptions = (options) =&gt; [...];

export <span class="md-code-keyword">default</span> async (options) =&gt; {
  <span class="md-code-keyword">try</span> {
    validateOptions(options);

    <span class="md-code-keyword">const</span> db = await connectToMongoDB();
    <span class="md-code-keyword">const</span> userId = await createUser(db.users, options.body);
    <span class="md-code-keyword">const</span> customerOnStripe = await createCustomerOnStripe(options.body.email);

    await createCustomer(db.customers, userId, customerOnStripe.id);
    sendWelcomeEmail(options.body.email);
    logCustomerOnSlack(options.body.email);
    trackEventInAnalytics(userId);
  } <span class="md-code-keyword">catch</span> (exception) {
    <span class="md-code-keyword">throw</span> <span class="md-code-keyword">new</span> <span class="md-code-built_in">Error</span>(`[signup] ${exception.message}`);
  }
};
</code></pre> <p>This means that instead of wasting your own time and energy, you can implement features and fix bugs with very little stress. The end result is a happier you and happier stakeholders. Good deal, right?</p> <p>While it&#x2019;s a minor detail, just so it&#x2019;s clear, let&#x2019;s look at how we actually <em>use</em> our action back in our API:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import signup from <span class="md-code-string">&apos;/path/to/signup/action&apos;</span>;

export <span class="md-code-keyword">default</span> {
  v1: {
    <span class="md-code-string">&apos;/users/signup&apos;</span>: (request, response) =&gt; {
      <span class="md-code-keyword">return</span> signup({ body: request.body, response });
    },  
  },
};
</code></pre> <p>This would be an appropriate time for a Bill Cosby &#x201C;puddin&#x2019; face&#x201D; GIF, but, well&#x2026;you know.</p> <h3 id="testing-our-action">Testing our action</h3> <p>The final &#x201C;wow&#x201D; of actions is how easy they are to test. Because the code is already in steps, an action tells us what we need to test. Assuming we&#x2019;ve mocked the functions in use inside of our action (e.g., <code class="md-code md-code-inline">stripe.customers.create</code>) an integration test for our action might look like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import signup from <span class="md-code-string">&apos;/path/to/signup/action&apos;</span>;
import stripe from <span class="md-code-string">&apos;/path/to/stripe&apos;</span>;
import slackLog from <span class="md-code-string">&apos;/path/to/slackLog&apos;</span>;

<span class="md-code-keyword">const</span> testUser = {
  email: <span class="md-code-string">&apos;test@test.com&apos;</span>,
  password: <span class="md-code-string">&apos;password&apos;</span>,
  profile: { name: <span class="md-code-string">&apos;Test User&apos;</span> },
};

describe(<span class="md-code-string">&apos;signup.js&apos;</span>, () =&gt; {
  beforeEach(() =&gt; {
    stripe.customers.create.mockReset();
    stripe.customers.create.mockImplementation(() =&gt; <span class="md-code-string">&apos;user123&apos;</span>);

    slackLog.success.mockReset();
    slackLog.success.mockImplementation();
  });

  test(<span class="md-code-string">&apos;creates a customer on stripe&apos;</span>, () =&gt; {
    signup({ body: testUser });
    expect(stripe.customers.create).toHaveBeenCalledTimes(<span class="md-code-number">1</span>);
    expect(stripe.customers.create).toHaveBeenCalledWith({ email: testUser.email });
  });

  test(<span class="md-code-string">&apos;logs the new customer on slack&apos;</span>, () =&gt; {
    signup({ body: testUser });
    expect(slackLog.success).toHaveBeenCalledTimes(<span class="md-code-number">1</span>);
    expect(slackLog.success).toHaveBeenCalledWith({
      message: <span class="md-code-string">&apos;New Customer&apos;</span>,
      metadata: {
        emailAddress: testUser.email,
      },
    });
  });
});
</code></pre> <p>Here, each test represents a verification that the step in our action completed as expected. Because we only care that our action performed the steps, our test suite is dirt simple. All we need to do is make a call to our action with some input (in this case, we pass a <code class="md-code md-code-inline">testUser</code> object as the <code class="md-code md-code-inline">options.body</code> value in our action).</p> <p>Next, we verify that our steps complete. Here, we verify that given a user with an email <code class="md-code md-code-inline">test@test.com</code>, our action calls to <code class="md-code md-code-inline">stripe.customers.create</code> passing that same email. Similarly, we test to see of our <code class="md-code md-code-inline">slackLog.success</code> method was called, passing the message we&#x2019;d like to see in our logs.</p> <p>There&#x2019;s ample nuance with testing, of course, but hopefully the point here is clear: we have a very tidy chunk of code that&#x2019;s remarkably easy to test. No confusion. No time wasted &#x201C;figuring it out.&#x201D; The only true cost would be the time mocking out the code called by our action if we hadn&#x2019;t done that already.</p> <h3 id="wrapping-up">Wrapping Up</h3> <p>So there you have it! Actions are a wonderful way to clean up your codebase, make things more predictable, and save yourself a ton of time in the process.</p> <p>Because actions are just a JavaScript pattern, the cost to test them out in your own app is zero. Try it, see if you like it. Most importantly: see if they improve the quality of your code. If you&#x2019;re struggling to write code that performs predictably, give this pattern a try. You won&#x2019;t regret it.</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/action-pattern-clean-obvious-testable-code</link><guid isPermaLink="true">https://ponyfoo.com/articles/action-pattern-clean-obvious-testable-code</guid><category><![CDATA[patterns]]></category><category><![CDATA[action-pattern]]></category><dc:creator><![CDATA[ryan.glover@cleverbeagle.com (Ryan Glover)]]></dc:creator><pubDate>Tue, 26 Mar 2019 12:02:06 GMT</pubDate></item><item><title><![CDATA[React State: Choose Wisely]]></title><description><![CDATA[<div class="f-core md-markdown"><p>Proper state handling in React will make your components simple and maintainable. Poor choices will give you lots of headaches in the long-term.</p><p>There are plenty of options for managing state in a React app. But there&#x2019;s very little guidance about which one you should use in any situation.</p> <p>Let&#x2019;s fix that.</p> <p>The solution you pick to manage state should fit they way you want to <em>use</em> the items you hold in state. Your choice should also make it easy for you to update, reuse, and refactor your code. In other words, your choice of state management can make your life easy or hard.</p><p>Here&#x2019;s a non-exhaustive list of things you should consider:</p> <ol> <li>How quickly can you add new state code?</li> <li>How well encapsulated is your stateful data?</li> <li>How easy is it to pull the data into a new component?</li> </ol> <p>Let&#x2019;s use this list as a basis for comparing different ways of handling state. To do this, we&#x2019;ll make the same simple component using three different forms of state handling. By making the same thing three ways, you&#x2019;ll get a few nice perspectives.</p> <p>First, you&#x2019;ll see that any approach can solve any problem. This is important because if you know that you can solve nearly any problem with any form of state management, you can turn your attention from the technical problem of getting it working, to the more abstract problem of making it work well in your over all application.</p> <p>Second, by making the same thing three ways, you&#x2019;ll start to see the subtle differences which can become big headaches as an applications grows.</p> <p>What are the three ways to manage state?</p> <ol> <li><strong>Local State</strong>: State stored directly on the component.</li> <li><strong>Global State</strong>: A global store. In this example, you&#x2019;ll use redux.</li> <li><strong>Context</strong>: A newer API that lets you access state higher up the tree.</li> </ol> <p>Using the three points of comparison and the three ways of managing state, you&#x2019;ll be able to build a nice table that will let you compare the different options.</p> <table> <thead> <tr> <th></th> <th>Local</th> <th>Global</th> <th>Context</th> </tr> </thead> <tbody> <tr> <td>Easy</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> <tr> <td>Encapsulated</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> <tr> <td>Available</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> </tbody> </table> <p>Alright. Let&#x2019;s get started.</p> <p>You&#x2019;ll be making a very simple counter. It&#x2019;s just two things: the current count and a button to add to the current count. As mentioned you&#x2019;ll have one for each type of state, but they all act exactly the same way.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/initial-e9c8dcc3726a4acba76a8f445c1fac9c.gif"></figure> <p>So far, nothing too exciting. The first thing you are going to do is make the presentation layer. This will be a simple reusable component that displays the count and the button. Making this independent is not only a good practice, it will also a show you how easy it is to swap different forms of state handling.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Display</span><span class="md-code-params">({ addOne, count, title})</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span> <span class="md-code-attribute">className</span>=<span class="md-code-value">&quot;counter&quot;</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">h3</span>&gt;</span>{title}<span class="md-code-tag">&lt;/<span class="md-code-title">h3</span>&gt;</span>
      Current Count: {count}
      <span class="md-code-tag">&lt;<span class="md-code-title">button</span> <span class="md-code-attribute">onClick</span>=<span class="md-code-value">{addOne}</span>&gt;</span> Add One! <span class="md-code-tag">&lt;/<span class="md-code-title">button</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>Now that you have your basic display component, you can start to build the stateful components.</p> <h3 id="local-state">Local State</h3> <p>The first component you&#x2019;ll build will store state locally, directly on the component.</p> <p>All you need is a basic class with <code class="md-code md-code-inline">state</code> as a property and a method to add to state. Pass both the <code class="md-code md-code-inline">count</code> from the state object and the <code class="md-code md-code-inline">addOne()</code> method into your presentation component and you&#x2019;re all done.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React, { Component } from <span class="md-code-string">&apos;react&apos;</span>;
import Display from <span class="md-code-string">&apos;../Display/Display&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-keyword">class</span> StateCounter extends Component {
  state = {
    count: <span class="md-code-number">0</span>
  }

  addOne = () =&gt; {
    <span class="md-code-keyword">this</span>.setState(state =&gt; ({count: state.count + <span class="md-code-number">1</span>}))
  }

  render() {
    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">Display</span>
        <span class="md-code-attribute">addOne</span>=<span class="md-code-value">{this.addOne}</span>
        <span class="md-code-attribute">count</span>=<span class="md-code-value">{this.state.count}</span>
        <span class="md-code-attribute">title</span>=<span class="md-code-value">&quot;Local Counter&quot;</span>
      /&gt;</span>
    )
  }
}
</span></code></pre> <p>At this point, one thing should jump out: it&#x2019;s easy. Even without knowing much about other forms of state handling, you can see this one is very simple to set up. But it gets even better.</p> <p>With the introduction of <a href="https://medium.com/r/?url=https%3A%2F%2Freactjs.org%2Fdocs%2Fhooks-overview.html" target="_blank" rel="noopener noreferrer">React Hooks</a>, you&#x2019;ll remove nearly all the boilerplate code.</p> <p>Here&#x2019;s an updated version using React hooks that&#x2019;s nearly identical to the official docs:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React, { useState } from <span class="md-code-string">&apos;react&apos;</span>;
import Display from <span class="md-code-string">&apos;../Display/Display&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">StateCounterHooks</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">const</span> [count, add] = useState(<span class="md-code-number">0</span>);
  <span class="md-code-keyword">const</span> addOne = () =&gt; add(count + <span class="md-code-number">1</span>);
  <span class="md-code-keyword">return</span> (
    <span><span class="md-code-tag">&lt;<span class="md-code-title">Display</span>
      <span class="md-code-attribute">addOne</span>=<span class="md-code-value">{addOne}</span>
      <span class="md-code-attribute">count</span>=<span class="md-code-value">{count}</span>
      <span class="md-code-attribute">title</span>=<span class="md-code-value">&quot;Local Counter&quot;</span>
    /&gt;</span>
  )
}
</span></code></pre> <p>As above, <code class="md-code md-code-inline">count</code> is the stateful data, and <code class="md-code md-code-inline">add()</code> is a method for updating state. It would really be difficult to make this easier.</p> <h3 id="global-state">Global State</h3> <p>The next component you&#x2019;ll build will use a global store. In this example, you&#x2019;ll use redux since it&#x2019;s by far the most popular, but many of the issues would be the same if you were using other options like MobX or even Apollo and GraphQL.</p> <p>We&#x2019;ll move quickly past some of the complexities of implementing redux. If you need a refresher, check out the <a href="https://medium.com/r/?url=https%3A%2F%2Fredux.js.org%2F" target="_blank" rel="noopener noreferrer">official docs</a>.</p> <p>To start make an action to add an item to the global store.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">add</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span> ({
    type: <span class="md-code-string">&apos;ADD_ONE&apos;</span>,
  })
}
</code></pre> <p>Next, make a reducer to actually store the count.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">count</span><span class="md-code-params">(state = 0, action)</span> </span>{
  <span class="md-code-keyword">switch</span> (action.type) {
    <span class="md-code-keyword">case</span> <span class="md-code-string">&apos;ADD_ONE&apos;</span>:
      <span class="md-code-keyword">return</span> state + <span class="md-code-number">1</span>;
    <span class="md-code-keyword">default</span>:
      <span class="md-code-keyword">return</span> state;
  }
}

export <span class="md-code-keyword">default</span> combineReducers({
  count,
})
</code></pre> <p>If you&#x2019;ve worked with redux before you may have ignored this part, but it&#x2019;s important to take a moment to think about what you just did here.</p> <p>You just created a global state object with the a property of <code class="md-code md-code-inline">count</code>&#xA0;. That property name is now unique for this particular action. If you wanted to create another property with a similar name you&#x2019;d need to either give it a unique name such as <code class="md-code md-code-inline">clickCount</code> or encapsulate one, the other, or both inside a nested object.</p> <p>You&#x2019;ll return to this idea in a bit. For now, use <code class="md-code md-code-inline">connect</code> to pull the state into your display component.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import { connect } from <span class="md-code-string">&apos;react-redux&apos;</span>

import { add } from <span class="md-code-string">&apos;../../store/actions&apos;</span>;
import Display from <span class="md-code-string">&apos;../Display/Display&apos;</span>;

export <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">ReduxCounter</span><span class="md-code-params">({ addOne, count })</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">Display</span>
      <span class="md-code-attribute">addOne</span>=<span class="md-code-value">{addOne}</span>
      <span class="md-code-attribute">count</span>=<span class="md-code-value">{count}</span>
      <span class="md-code-attribute">title</span>=<span class="md-code-value">&quot;Redux Counter&quot;</span>
    /&gt;</span>
  )
}

const mapStateToProps = ({ count }) =&gt; ({ count });

const mapDispatchToProps = { addOne: add }

export default connect(mapStateToProps, mapDispatchToProps)(ReduxCounter);
</span></code></pre> <p>Again, we won&#x2019;t explore the details, but as a summary, the component is tapping into the global store so that you can both read from it, using <code class="md-code md-code-inline">mapStateToProps</code>, and update it using <code class="md-code md-code-inline">mapDispatchToProps</code>. Other than that, your display component doesn&#x2019;t care where the data comes from.</p> <p>So far, the thing that should jump out immediately is how much work is involved. There are more files, more imports, more complexity. That&#x2019;s not a problem if the advantages outweigh the extra work&#x200A;&#x2014;&#x200A;and they often will&#x200A;&#x2014;&#x200A;but it is still something to consider.</p> <h3 id="context">Context</h3> <p>The final component will use context. This is a fairly new form of state handling that was introduced in React 16.3. Context is similar to local state in many ways, but it has a slightly different implementation.</p> <p>To start off, you need to create a context. This will set up a couple defaults that you will use when you create an implementation.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;

<span class="md-code-keyword">const</span> CounterContext = React.createContext({
  addOne(){},
  counter: <span class="md-code-number">0</span>,
});

export <span class="md-code-keyword">default</span> CounterContext;
</code></pre> <p>This sets the initial state of <code class="md-code md-code-inline">0</code> and adds a noop function that you will override later.</p> <p>To actually use context, you will first need to make a <strong>Provider</strong>. A provider is the base component that will hold the state object and the methods for updating the state.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React, { Component } from <span class="md-code-string">&apos;react&apos;</span>;
import CounterContext from <span class="md-code-string">&apos;./CounterContext&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-keyword">class</span> ContextProvider extends Component {
  state = {
    count: <span class="md-code-number">0</span>,
  }

  addOne = () =&gt; {
    <span class="md-code-keyword">this</span>.setState(state =&gt; ({count: state.count + <span class="md-code-number">1</span>}))
  }
  
  render() {
    <span class="md-code-keyword">const</span> { count } = <span class="md-code-keyword">this</span>.state;

    <span class="md-code-keyword">const</span> value = {
      addOne: <span class="md-code-keyword">this</span>.addOne,
      count,
    };

    <span class="md-code-keyword">return</span> (
      <span><span class="md-code-tag">&lt;<span class="md-code-title">CounterContext.Provider</span> <span class="md-code-attribute">value</span>=<span class="md-code-value">{value}</span>&gt;</span>
        {this.props.children}
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterContext.Provider</span>&gt;</span>
    )
  }
}
</span></code></pre> <p>If you scroll up, you&#x2019;ll notice this looks very similar to the local state component. The big difference is that you are combing the state and the update function in single object which you then pass down into a <strong>Provider</strong> component that wraps everything else. In this case, you are wrapping <code class="md-code md-code-inline">this.props.children</code> but you can also wrap a component directly.</p> <p>To use this component, you&#x2019;d wrap some other components. In this case, you&#x2019;ll wrap a single component.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">
import React from <span class="md-code-string">&apos;react&apos;</span>;
import CounterProvider from <span class="md-code-string">&apos;../ContextCounter/CounterProvider&apos;</span>;
import OtherComponent from <span class="md-code-string">&apos;../ContextCounter/OtherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Counter</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>Notice, you are not passing any props to <code class="md-code md-code-inline">OtherComponent</code>&#xA0;. More clearly, you are not <em>explicitly</em> passing anything to <code class="md-code md-code-inline">OtherComponent</code>. The provider is holding the state information so that you can tap into it later.</p> <p>Even if you go several components deep, you will still be able to access the provider&#x2019;s state. So if <code class="md-code md-code-inline">OtherComponent</code> returns another component:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import AnotherComponent from <span class="md-code-string">&apos;./AnotherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">OtherComponent</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">AnotherComponent</span> /&gt;</span>
  )
}
</span></code></pre> <p>And <code class="md-code md-code-inline">AnotherComponent</code> returns yet <em>another</em> component. That information is still hanging in the background.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import ContextDisplay from <span class="md-code-string">&apos;./ContextDisplay&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">AnotherComponent</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">ContextDisplay</span> /&gt;</span>
  )
}
</span></code></pre> <p>Ok, that&#x2019;s far enough. It&#x2019;s time to pull out the state and do something with it.</p> <p>To get the information you need to create a <strong>Consumer</strong>. The consumer uses render props to pull out that single value object. Remember, <code class="md-code md-code-inline">value</code> contains <code class="md-code md-code-inline">count</code> and <code class="md-code md-code-inline">addOne</code>&#xA0;. You can use destructuring as a short hand.</p> <p>Now that you have the state and the function to update state, you can pass it into your display component:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import CounterContext from <span class="md-code-string">&apos;./CounterContext&apos;</span>;
import Display from <span class="md-code-string">&apos;../Display/Display&apos;</span>

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">ContextDisplay</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">CounterContext.Consumer</span>&gt;</span>
      {({ addOne, count}) =&gt; {
        return (
          <span class="md-code-tag">&lt;<span class="md-code-title">Display</span>
            <span class="md-code-attribute">addOne</span>=<span class="md-code-value">{addOne}</span>
            <span class="md-code-attribute">count</span>=<span class="md-code-value">{count}</span>
            <span class="md-code-attribute">title</span>=<span class="md-code-value">&quot;Context Counter&quot;</span>
          /&gt;</span>
        )
      }}
    <span class="md-code-tag">&lt;/<span class="md-code-title">CounterContext.Consumer</span>&gt;</span>
  )
}
</span></code></pre> <h3 id="comparisons">Comparisons</h3> <p>As you saw, any form of state handling can do the same thing. In reality, if you put effort into it, you can make any state handling system work for any piece of data in your application.</p> <p>Of course, who wants to put effort into it? The goal is always less effort, better results. With that in mind, it&#x2019;s time to compare the different approaches.</p> <h3 id="simplicity">Simplicity</h3> <p>This is the easiest comparison. Nothing beats local state particularly when you use React hooks. It&#x2019;s quick, it does that job and it&#x2019;s easy to refactor into a more complicated system if necessary. As a rule, you should always start with local state. If you need something more complicated, you can refactor as you go.</p> <p>For this reason, Local State wins for simplicity.</p> <table> <thead> <tr> <th></th> <th>Local</th> <th>Global</th> <th>Context</th> </tr> </thead> <tbody> <tr> <td>Easy</td> <td></td> <td></td> <td></td> </tr> <tr> <td>Encapsulated</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> <tr> <td>Available</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> </tbody> </table> <h3 id="encapsulation">Encapsulation</h3> <p>Next, let&#x2019;s explore how well the data is encapsulated. If data is well encapsulated, you can reuse a component multiple times without any concern that another component may accidentally change your state. Encapsulation isn&#x2019;t necessarily a goal in itself. There are times where you absolutely want different components to access and modify shared data (more on that in the next section).</p> <p>As with most things, it&#x2019;s about predictability. If you expect a component to be independent, the data shouldn&#x2019;t be open to modifications by other components.</p> <p>To explore encapsulation, you&#x2019;ll need to add multiple versions of a component to see how they interact.</p> <p>To start off return to the original page that has all three components.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/multicount-26b623cdb35448a4a979a51799f41c2b.gif"></figure> <p>The code looks something like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import StateCounter from <span class="md-code-string">&apos;../StateCounter/StateCounterHooks&apos;</span>;
import ReduxCounter from <span class="md-code-string">&apos;../ReduxCounter/ReduxCounter&apos;</span>;
import CounterProvider from <span class="md-code-string">&apos;../ContextCounter/CounterProvider&apos;</span>;
import OtherComponent from <span class="md-code-string">&apos;../ContextCounter/OtherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Counter</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">StateCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ReduxCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>You have a simple parent component that wraps one instance of each component. To test encapsulation, all you need to do is add a second instance.</p> <p>Start off with the <code class="md-code md-code-inline">StateCounter</code> component. The updated code looks like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import StateCounter from <span class="md-code-string">&apos;../StateCounter/StateCounterHooks&apos;</span>;
import ReduxCounter from <span class="md-code-string">&apos;../ReduxCounter/ReduxCounter&apos;</span>;
import CounterProvider from <span class="md-code-string">&apos;../ContextCounter/CounterProvider&apos;</span>;
import OtherComponent from <span class="md-code-string">&apos;../ContextCounter/OtherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Counter</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">StateCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">StateCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ReduxCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>When you open that in the browser, you can easily see that the data is well encapsulated. Every time you click the add button, it only updates that count on that particular component.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/doublelocal-2621a9c3317a4193b1a3bc77a5f2e907.gif"></figure> <p>Now try adding a second redux component.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import StateCounter from <span class="md-code-string">&apos;../StateCounter/StateCounterHooks&apos;</span>;
import ReduxCounter from <span class="md-code-string">&apos;../ReduxCounter/ReduxCounter&apos;</span>;
import CounterProvider from <span class="md-code-string">&apos;../ContextCounter/CounterProvider&apos;</span>;
import OtherComponent from <span class="md-code-string">&apos;../ContextCounter/OtherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Counter</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">StateCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ReduxCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ReduxCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
          <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>Open this in the browser and notice how different it is. Every time you click on **Add&#xA0;**, you update the data on both components.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/doubleglobal-d6e57f1ba52d49e4b2507e2af0c71a93.gif"></figure> <p>In this case, these components are not independent. You can alter the data in any component that use the same action.</p> <p>Now, there are certainly ways around this. You can have an <code class="md-code md-code-inline">id</code> on the global store for each component. You can use an array. You can use a different namespace. But the fact is that global stores are best for global data. Encapsulation is not a primary goal.</p> <p>How about context? Well, this is were things get a little more tricky. What does it mean to add a second component? Does that mean you add a second consumer? In this case, that would mean adding a second <code class="md-code md-code-inline">OtherComponent</code> under the same provider. Or do you add a second <code class="md-code md-code-inline">CounterProvider</code>?</p> <p>Why not both? First, add another provider. Then in the existing provider, add a second instance of <code class="md-code md-code-inline">OtherComponent</code>.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import React from <span class="md-code-string">&apos;react&apos;</span>;
import StateCounter from <span class="md-code-string">&apos;../StateCounter/StateCounter&apos;</span>;
import ReduxCounter from <span class="md-code-string">&apos;../ReduxCounter/ReduxCounter&apos;</span>;
import CounterProvider from <span class="md-code-string">&apos;../ContextCounter/CounterProvider&apos;</span>;
import OtherComponent from <span class="md-code-string">&apos;../ContextCounter/OtherComponent&apos;</span>;

export <span class="md-code-keyword">default</span> <span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">Counter</span><span class="md-code-params">()</span> </span>{
  <span class="md-code-keyword">return</span>(
    <span><span class="md-code-tag">&lt;<span class="md-code-title">div</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">StateCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">ReduxCounter</span> /&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
      <span class="md-code-tag">&lt;<span class="md-code-title">CounterProvider</span>&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
        <span class="md-code-tag">&lt;<span class="md-code-title">OtherComponent</span> /&gt;</span>
      <span class="md-code-tag">&lt;/<span class="md-code-title">CounterProvider</span>&gt;</span>
    <span class="md-code-tag">&lt;/<span class="md-code-title">div</span>&gt;</span>
  )
}
</span></code></pre> <p>Let&#x2019;s see what happens.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/multicontext-7ab077100e344cdcb678ad43de92ee08.gif"></figure> <p>When you click on the first component, the counter iterates, but the other counters do not. But when you click on the second context counter, it <em>will</em> change the third counter. Similarily, if you click on the third counter, it will change the second.</p> <p>This means that context is somewhere in the middle. It can be encapsulated when you want it to be, and it can be global when you want it to be.</p> <p>Now, this claim comes with a big caveat. The ability to share date or keep it private all depends on how you order the components. Anything in the same hierarchy as the provider will share data, anything outside will not. This means that you may have to move providers up or down depending on how you want to use the consumers.</p> <p>Still, at this point, you can finish filling out the chart. Local state is well encapsulated and context can be. They each get a check. Global state is not well encapsulated to it does not get credit.</p> <table> <thead> <tr> <th></th> <th>Local</th> <th>Global</th> <th>Context</th> </tr> </thead> <tbody> <tr> <td>Easy</td> <td></td> <td></td> <td></td> </tr> <tr> <td>Encapsulated</td> <td></td> <td></td> <td></td> </tr> <tr> <td>Available</td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> <td><code class="md-code md-code-inline">?</code></td> </tr> </tbody> </table> <h3 id="availability">Availability</h3> <p>That just leaves one more area of concern: availability. The beauty part is that you don&#x2019;t need to write any more code. Sometimes you want your data to be easy to access.</p> <p>Consider a shopping cart. Pushing users to a sale is the most important thing your code can do. Consequently, nearly every part of your app will potentially need to know about the cart (how much is in it, the total, etc). This is data you want to make easily accessible.</p> <p>In this case, you can use what you learned above. Local state, for example, could work, but you would have to store it so far up the component hierarchy that you&#x2019;d be passing it down a lot of props.</p> <p>Global state, on the other hand, was designed specifically for this. It&#x2019;s an easy thumbs up .</p> <p>Context as you saw above can either be global (at least depending on where you put it in the hierarchy) or encapsulated. Go ahead and give it credit.</p> <p>The final comparison looks like this:</p> <table> <thead> <tr> <th></th> <th>Local</th> <th>Global</th> <th>Context</th> </tr> </thead> <tbody> <tr> <td>Easy</td> <td></td> <td></td> <td></td> </tr> <tr> <td>Encapsulated</td> <td></td> <td></td> <td></td> </tr> <tr> <td>Available</td> <td></td> <td></td> <td></td> </tr> </tbody> </table> <p>One thing that&#x2019;s not reflected in this chart is the large collection of third-party code that ties in with a global store. If you want to use observables with your store, then you&#x2019;re better off using <a href="https://medium.com/r/?url=https%3A%2F%2Fgithub.com%2Fredux-observable%2Fredux-observable" target="_blank" rel="noopener noreferrer">redux-observables</a> than trying to roll your own solution.</p> <h3 id="building-and-refactoring">Building and Refactoring</h3> <p>You may look at this table and think that local state or context are somehow better. That&#x2019;s not true. The best solution is the one that works best for your data and your app.</p> <p>Still, there are a few common patterns that may help the decision making process.</p> <p>First, always start with local state. As you saw above, local state is the easiest to build and prototype. When you start with local state you don&#x2019;t have to deal with all the complexities of setting up a <code class="md-code md-code-inline">Provider</code> or writing actions and reducers. Even if you plan on moving to a more widely available state management, local state is a great way prove your concept.</p> <p>Next, use context if you want to maximize reusability and local state is not sufficient. After you go through the hard work of building a component, you&#x2019;ll want to reuse it as much as possible. That may even mean reusing it across different project and teams.</p> <p>By choosing context, you reduce the number of additional dependencies your independent component will need. In other words, it will be much easier to drop the new component into an existing project. The component will be internally independent. You gain all the advantages of widely available components while avoiding extra code.</p> <p>Finally, don&#x2019;t worry about the rules. If your team uses global state and has a well defined structure in place, you&#x2019;ll do more harm by using a different solution. Internal consistency and clear communication should always be the first priority. If your new component breaks the pattern, you may save yourself a few minutes of coding, but your teammates will lose their ability to quickly skim and understand a component. New patterns aren&#x2019;t bad, but they should add value.</p> <p>Each of these solutions exist for a reason. Your job is to find the fit that makes your code easiest to build and maintain.</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/react-state-choose-wisely</link><guid isPermaLink="true">https://ponyfoo.com/articles/react-state-choose-wisely</guid><category><![CDATA[javascript]]></category><category><![CDATA[react]]></category><category><![CDATA[redux]]></category><dc:creator><![CDATA[jsmapr1@gmail.com (Joe Morgan)]]></dc:creator><pubDate>Tue, 19 Mar 2019 13:22:59 GMT</pubDate></item><item><title><![CDATA[GraphQL in Depth: What, Why, and How]]></title><description><![CDATA[<div class="f-core md-markdown"><p>GraphQL is all the rage, but what exactly is it? In this in-depth walkthrough, we take a look at what GraphQL is, how to use it, and why you should use it.</p><p>Learn why GraphQL is all the rage! We&#x2019;ll walk through the implementation of a schema for a popcorn company&#x2019;s API, learning about types, queries, and mutations as we go.</p><p>I love popcorn. </p> <p>As I&#x2019;m writing this, I&#x2019;m snacking on a bowl of multi-colored kernels, lightly seasoned with Flavacol (hint: this is the movie theater&#x2019;s secret) and I&#x2019;m feeling a twinge of hubris in my eye&#x2026;</p> <p>&#x201C;I should start a popcorn company&#x2026;&#x201D;</p> <p>Well, that&#x2019;s a terrible idea, Ryan! But, for the sake of this tutorial, it&#x2019;s <em>the best idea you&#x2019;ve had all week</em>. <strong>In this tutorial, we&#x2019;re going to learn how to create a simple, GraphQL-based API for a popcorn company</strong>.</p> <p>All we need is a name. Seeing as how I&#x2019;m quite fond of sarcasm, how about&#x2026;</p> <p><em>Sarcastic Kernels: The Popcorn You Love to Hate to Eat&#x2122;</em></p> <p>Perfect! And like any self-respecting nerd, it only seems right to start writing some code before we come up with a sound business plan. To get started, let&#x2019;s wrap our heads around what GraphQL is and how it compares to REST.</p> <h1 id="what-is-graphql">What is GraphQL?</h1> <p>GraphQL is a client-side query language coupled with a pattern &#x2014; formally known as a &#x201C;schema&#x201D; &#x2014; for organizing the creation, reading, updating, and deleting of data in your application (yeah, that CRUD).</p> <p>We say &#x201C;application&#x201D;, here, and not database because GraphQL is data-source-agnostic, meaning it doesn&#x2019;t care <em>where</em> your data lives.</p> <p>From the outside looking in, GraphQL can seem quite scary. Does the &#x201C;Graph&#x201D; part mean I have to learn about graph databases? Does the QL (query language) mean I have to learn I have to learn a brand new programming language?!</p> <p>Not quite. To calm your nerves, the harsh truth is: GraphQL is just a dressed up <code class="md-code md-code-inline">GET</code> or <code class="md-code md-code-inline">POST</code> request.</p> <p>Wait, what?!</p> <p>Yep. While GraphQL as a whole <em>does</em> introduce some new concepts for organizing and interacting with your data, behind the curtain, GraphQL still relies on a good ol&#x2019; fashioned HTTP request to do its magic.</p> <h3 id="rethinking-rest">Rethinking REST</h3> <p>Where GraphQL separates from a more familiar REST API is due to its <em>flexibility</em>. With REST, done properly, endpoints are typically designed from the perspective of a resource, or, a type of data in our application.</p> <p>For example, a <code class="md-code md-code-inline">GET</code> request to <code class="md-code md-code-inline">/api/v1/flavors</code> would be expected to send us back a response that looks something like this:</p> <pre class="md-code-block"><code class="md-code md-lang-json">[
  {
   &quot;<span class="md-code-attribute">id</span>&quot;: <span class="md-code-value"><span class="md-code-number">1</span></span>,
    &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The Lazy Person&apos;s Movie Theater&quot;</span></span>,
    &quot;<span class="md-code-attribute">description</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!&quot;</span>
  </span>}, {
    &quot;<span class="md-code-attribute">id</span>&quot;: <span class="md-code-value"><span class="md-code-number">2</span></span>,
    &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;What&apos;s Wrong With You Caramel&quot;</span></span>,
    &quot;<span class="md-code-attribute">description</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;You&apos;re a crazy person that likes sweet popcorn. Congratulations.&quot;</span>
  </span>}, {
    &quot;<span class="md-code-attribute">id</span>&quot;: <span class="md-code-value"><span class="md-code-number">3</span></span>,
    &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;Gnarly Chili Lime&quot;</span></span>,
    &quot;<span class="md-code-attribute">description</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The kind of popcorn you make when you need a good smack in the face.&quot;</span></span>}
]
</code></pre> <p>Now, there&#x2019;s nothing terribly wrong with this, but let&#x2019;s consider our UI, or rather, how we intend to <em>consume</em> this data.</p> <p>If we wanted to display a simple list UI where all we had were the kinds of popcorn that are available (and nothing else), we might end up with a design like the one below.</p> <figure><img alt class src="https://cleverbeagle-cdn.s3.amazonaws.com/ponyfoo/graphql-example-list-ui.png"></figure> <p>And&#x2026; we&#x2019;d be in a bit of a jam. We can choose not to use the <code class="md-code md-code-inline">description</code> field, sure, but are we just going to sit around and act like we <em>didn&#x2019;t</em> send that to the client as well?!</p> <p>Haha! What do you think this is? <em>Fantasy land</em>? Of course we are! And when someone asks &#x201C;why is the app so slow for users?&#x201D; in a few months, we&#x2019;ll just say &#x201C;be right back, going to grab a coffee!&#x201D; and then split town, never to be seen again.</p> <p>It&#x2019;s not entirely our fault, though. To be fair, REST <em>is</em> the data-fetching equivalent of going to a restaurant and being asked &#x201C;What do you want? We&#x2019;ll give you what we have.&#x201D;</p> <p>Jokes aside, in a real application this can be problematic. For example, we may display different traits for each kind of popcorn like pricing information, brand details, or dietary restrictions (&#x201C;vegan popcorn!&#x201D;). Rigid REST endpoints make delivering specific traits based on context a headache, leading to unnecessary performance overhead and frustration.</p> <h2 id="how-graphql-improves-rest">How GraphQL improves REST</h2> <p>On the surface, this may seem like a small problem. &#x201C;Who cares if we&#x2019;re sending unnecessary data to the client?&#x201D; Well, let&#x2019;s add some context. GraphQL was invented at Facebook. Facebook serves millions of requests <em>per second</em>.</p> <p>Translation? Every optimization counts.</p> <p>Instead of saying &#x201C;here&#x2019;s what&#x2019;s available,&#x201D; <strong>GraphQL inverts the problem and asks &#x201C;what do you need?&#x201D;</strong></p> <p>We can get back a response from GraphQL specific to the context where we&#x2019;re consuming data <em>without</em> having to add a one-off endpoint, perform multiple requests, or write convoluted conditional code.</p> <h1 id="how-does-graphql-work">How does GraphQL work?</h1> <p>Like we hinted at above, at it&#x2019;s core GraphQL relies on a simple <code class="md-code md-code-inline">GET</code> or <code class="md-code md-code-inline">POST</code> request for moving data to and <em>from</em> the client. Unpacking that, GraphQL has two types of requests when it comes to reading (the R in CRUD), creating, updating, and deleting (The CUD in CRUD): queries and mutations.</p> <p>All of those queries and mutations are sent as either a <code class="md-code md-code-inline">GET</code> or <code class="md-code md-code-inline">POST</code> request to a GraphQL server at a URL like <code class="md-code md-code-inline">https://myapp.com/graphql</code>. More on that below.</p> <h3 id="understanding-queries">Understanding Queries</h3> <p>Queries are what you&#x2019;d expect: a request for some data. We have the UI, and we need to fill it with data, so we make a <em>query</em> to the server. With a traditional REST API, our query would come in the form of a GET request. With GraphQL, we introduce a new syntax for requesting data:</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    name
  }
}
</code></pre> <p>Wait, you mean JSON? Or is that a JavaScript object?</p> <p>Neither.</p> <p>The &#x201C;QL&#x201D; part of GraphQL stands for <em>query language</em>. Quite literally, this is a brand new language for writing data queries. That sounds more complicated than it is. Let&#x2019;s break down the above query.</p> <pre class="md-code-block"><code class="md-code">{
  // The fields we want to query are written here.
}
</code></pre> <p>All queries start from the &#x201C;root query&#x201D; and are known as fields. To save yourself a headache: it&#x2019;s best to refer to this as &#x201C;query fields in my schema.&#x201D; Don&#x2019;t fret, we&#x2019;ll learn more about how those are defined in a bit. Here, we ask to query the <code class="md-code md-code-inline">flavors</code> field on the root query.</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    // The sub-fields we want for each flavor are written here.
  }
}
</code></pre> <p>When querying a field, we also need to specify the sub-fields we want for each object in the response (even if we expect just a single object to be returned).</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    name
  }
}
</code></pre> <p>The end result? When we send this query to a GraphQL server, we get back a neat, tidy response like this:</p> <pre class="md-code-block"><code class="md-code md-lang-json">{
  &quot;<span class="md-code-attribute">data</span>&quot;: <span class="md-code-value">{
    &quot;<span class="md-code-attribute">flavors</span>&quot;: <span class="md-code-value">[
      { &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The Lazy Person&apos;s Movie Theater&quot;</span> </span>},
      { &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;What&apos;s Wrong With You Caramel&quot;</span> </span>},
      { &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;Gnarly Chili Lime&quot;</span> </span>}
    ]
  </span>}
</span>}
</code></pre> <p>Neat, right? To make this clear, if we ran the following query on another page:</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    id
    name
    description
  }
}
</code></pre> <p>we&#x2019;d get back a response like this:</p> <pre class="md-code-block"><code class="md-code md-lang-json">{
  &quot;data&quot;: {
    &quot;flavors&quot;: [
      { &quot;id&quot;: 1, &quot;name&quot;: &quot;The Lazy Person&apos;s Movie Theater&quot;, description: &quot;That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!&quot; },
      { &quot;id&quot;: 2, &quot;name&quot;: &quot;What&apos;s Wrong With You Caramel&quot;, description: &quot;You&apos;re a crazy person that likes sweet popcorn. Congratulations.&quot; },
      { &quot;id&quot;: 3, &quot;name&quot;: &quot;Gnarly Chili Lime&quot;, description: &quot;A friend told me this would taste good. It didn&apos;t. It burned my kernels. I haven&apos;t had the heart to tell him.&quot; }
    ]
  }
}
</code></pre> <p>Super powerful! <em>Same endpoint, with a different response tailored to the context</em>.</p> <p>If we wanted to get a single flavor, GraphQL queries accept arguments, too:</p> <pre class="md-code-block"><code class="md-code">{
  flavors(id: &quot;1&quot;) {
    id
    name
    description
  }
}
</code></pre> <p>Here, we&#x2019;ve hardcoded the specific <code class="md-code md-code-inline">id</code> of a flavor we want to query, but we can also have a dynamic <code class="md-code md-code-inline">id</code>:</p> <pre class="md-code-block"><code class="md-code">query getFlavor($id: ID) {
  flavors(id: $id) {
    id
    name
    description
  }
}
</code></pre> <p>On the first line, we give our query a name (this is arbitrary; we could replace <code class="md-code md-code-inline">getFlavor</code> with <code class="md-code md-code-inline">pizza</code> and this would still work) and define the variables that query expects. Here, we expect a possible variable <code class="md-code md-code-inline">id</code> to be passed as an <code class="md-code md-code-inline">ID</code> scalar type (more on these below).</p> <p>Regardless of using a static or dynamic <code class="md-code md-code-inline">id</code> to make our request, here&#x2019;s the response we can expect:</p> <pre class="md-code-block"><code class="md-code md-lang-json">{
  &quot;data&quot;: {
    &quot;flavors&quot;: [
      { &quot;id&quot;: 1, &quot;name&quot;: &quot;The Lazy Person&apos;s Movie Theater&quot;, description: &quot;That elusive flavor that you begrudgingly carted yourself to the theater for, now in the comfort of your own home, you slob!&quot; }
    ]
  }
}
</code></pre> <p>Nice! Hopefully your hamster is starting to spin. This is already great, but where GraphQL really starts to shine is with <em>nested fields</em>. Let&#x2019;s assume we had another field in our schema called <code class="md-code md-code-inline">nutrition</code> that told us just how unhealthy our sarcastic kernels are:</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    id
    name
    nutrition {
      calories
      fat
      sodium
    }
  }
}
</code></pre> <p>What this <em>may</em> look like is us having a nested <code class="md-code md-code-inline">nutrition</code> object on each of our <code class="md-code md-code-inline">flavors</code>. Not quite! With GraphQL, we can combine separate but related data sources into a single query, getting a response that gives us the benefit of nested data without having to denormalize everything in the database:</p> <pre class="md-code-block"><code class="md-code md-lang-json">{
  &quot;<span class="md-code-attribute">data</span>&quot;: <span class="md-code-value">{
    &quot;<span class="md-code-attribute">flavors</span>&quot;: <span class="md-code-value">[
      {
        &quot;<span class="md-code-attribute">id</span>&quot;: <span class="md-code-value"><span class="md-code-number">1</span></span>,
        &quot;<span class="md-code-attribute">name</span>&quot;: <span class="md-code-value"><span class="md-code-string">&quot;The Lazy Person&apos;s Movie Theater&quot;</span></span>,
        &quot;<span class="md-code-attribute">nutrition</span>&quot;: <span class="md-code-value">{
          &quot;<span class="md-code-attribute">calories</span>&quot;: <span class="md-code-value"><span class="md-code-number">500</span></span>,
          &quot;<span class="md-code-attribute">fat</span>&quot;: <span class="md-code-value"><span class="md-code-number">12</span></span>,
          &quot;<span class="md-code-attribute">sodium</span>&quot;: <span class="md-code-value"><span class="md-code-number">1000</span>
        </span>}
      </span>},
      ...
    ]
  </span>}
</span>}
</code></pre> <p>That&#x2019;s a serious boon on productivity. But what about <em>updating</em> data, does GraphQL give the same advantages?</p> <h3 id="understanding-mutations">Understanding Mutations</h3> <p>Where <em>queries</em> fetch data, <em>mutations</em> are responsible for making changes to data. Alternatively, too, mutations can be used for a generic RPC (remote procedure call) for miscellaneous tasks like sending a user&#x2019;s data to a third-party API.</p> <pre class="md-code-block"><code class="md-code">mutation updateFlavor($id: ID!, $name: String, $description: String) {
  updateFlavor(id: $id, name: $name, description: $description) {
    id
    name
    description
  }
}
</code></pre> <p>Mutations rely on a similar syntax to queries. Here, we define a mutation <code class="md-code md-code-inline">updateFlavor</code> with some variables: <code class="md-code md-code-inline">id</code>, <code class="md-code md-code-inline">name</code>, and <code class="md-code md-code-inline">description</code>. Just like with our queries, we &#x201C;wrap&#x201D; a mutation field (defined on a similar <em>root mutation</em>) using the keyword <code class="md-code md-code-inline">mutation</code>, followed by a name describing the mutation and a set of variables to pass along.</p> <p>Those variables include <em>what we&#x2019;re trying to change</em> or <em>mutate</em>. Notice, too, that after executing a mutation, we can ask for some fields <em>back</em>.</p> <p>In this case, we want to get back the <code class="md-code md-code-inline">id</code>, <code class="md-code md-code-inline">name</code>, and <code class="md-code md-code-inline">description</code> <em>after</em> they&#x2019;ve been mutated. This helps with things like optimistic UI, negating the need for a request following the update.</p> <h3 id="writing-a-schema-and-attaching-it-to-a-graphql-server">Writing a schema and attaching it to a GraphQL server</h3> <p>So far, what we&#x2019;ve been looking at is how GraphQL functions <em>on the client</em>. This is how we <em>make</em> requests, but how do we respond to them?</p> <h4 id="the-graphql-server">The GraphQL server</h4> <p>In order to make a GraphQL request, we need to have a GraphQL <em>server</em> to send it to. A GraphQL server is a regular ol&#x2019; HTTP server (if you&#x2019;re a JavaScripter, think Express or Hapi) with a GraphQL <em>schema</em> attached to it.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">import express from <span class="md-code-string">&apos;express&apos;</span>
import graphqlHTTP from <span class="md-code-string">&apos;express-graphql&apos;</span>
import schema from <span class="md-code-string">&apos;./schema&apos;</span>

<span class="md-code-keyword">const</span> app = express()

app.use(<span class="md-code-string">&apos;/graphql&apos;</span>, graphqlHTTP({
  schema: schema,
  graphiql: <span class="md-code-literal">true</span>
}))

app.listen(<span class="md-code-number">4000</span>)
</code></pre> <p>By &#x201C;attached,&#x201D; we mean that requests received by that server are passed through the schema and then back to the client, kind of like an air filter in your house.</p> <p>The &#x201C;filtering&#x201D; process that takes place is relative to the query or mutation you send from the client. Both queries and mutations are <em>resolved</em> using functions associated with the fields defined on our <em>root query</em> or <em>root mutation</em> in our schema.</p> <p>Above, we can see a mock HTTP server being created with the JavaScript library Express. Utilizing the <code class="md-code md-code-inline">graphqlHTTP</code> function from the <code class="md-code md-code-inline">express-graphql</code> package by Facebook, we &#x201C;attach&#x201D; our schema (here, assumed to be defined in another file) and start our server on port <code class="md-code md-code-inline">4000</code> (i.e., <code class="md-code md-code-inline">http://localhost:4000/graphql</code> is where we&#x2019;ll send requests from the client).</p> <h4 id="types-and-resolvers">Types and resolvers</h4> <p>With a running server, we need to define the schema that we attach to it.</p> <p>Recall that earlier, we talked about defining <em>fields</em> on either a <em>root query</em> or <em>root mutation</em>.</p> <pre class="md-code-block"><code class="md-code">import gql from &apos;graphql-tag&apos;
import mongodb from &apos;/path/to/mongodb&#x2019; // For example. Assuming `mongodb` gives us a MongoDB connection.

const schema = {
  typeDefs: gql`
    type Nutrition {
      flavorId: ID
      calories: Int
      fat: Int
      sodium: Int
    }

    type Flavor {
      id: ID
      name: String
      description: String
      nutrition: Nutrition
    }

    type Query {
      flavors(id: ID): [Flavor]
    }

    type Mutation {
      updateFlavor(id: ID!, name: String, description: String): Flavor
    }
  `,
  resolvers: {
    Query: {
      flavors: (parent, args) =&gt; {
        // Assuming args equals an object like { id: &apos;1&apos; }
        return mongodb.collection(&apos;flavors&apos;).find(args).toArray()
      },
    },
    Mutation: {
      updateFlavor: (parent, args) =&gt; {
        // Assuming args equals an object like { id: &apos;1&apos;, name: &apos;Movie Theater Clone&apos;, description: &apos;Bring the movie theater taste home!&apos; }

        // Perform the update.
        mongodb.collection(&apos;flavors&apos;).update(args)

        // Return the flavor after the update.
        return mongodb.collection(&apos;flavors&apos;).findOne(args.id)
      },
    },
    Flavor: {
      nutrition: (parent) =&gt; {
        return mongodb.collection(&apos;nutrition&apos;).findOne({
          flavorId: parent.id,
        })
      }
    },
  },
}

export default schema
</code></pre> <p>When it comes to defining fields in a GraphQL schema, there are two parts: <code class="md-code md-code-inline">typeDefs</code> and <code class="md-code md-code-inline">resolvers</code>.</p> <p><code class="md-code md-code-inline">typeDefs</code> contain the <em>type definitions</em> for the data in our application. For example, earlier we talked about retrieving a list of <code class="md-code md-code-inline">flavors</code>. In order to do that, we need to do three things:</p> <ol> <li>Tell our schema what a flavor&#x2019;s data looks like (above, by defining the <code class="md-code md-code-inline">type Flavor</code> type).</li> <li>Define a field on the root <code class="md-code md-code-inline">type Query</code> field (above, the <code class="md-code md-code-inline">flavors</code> property on the <code class="md-code md-code-inline">type Query</code> value).</li> <li>Define a <em>resolver function</em> on the <code class="md-code md-code-inline">resolvers.Query</code> object corresponding to the field we defined on the root <code class="md-code md-code-inline">type Query</code> field.</li> </ol> <p>Focusing on the <code class="md-code md-code-inline">typeDefs</code>, this is where we tell our schema about the <em>shape</em> of our data. In other words, we tell GraphQL about the different properties a piece of data might contain.</p> <pre class="md-code-block"><code class="md-code">type Flavor {
  id: ID
  name: String
  description: String
  nutrition: Nutrition
}
</code></pre> <p>The <code class="md-code md-code-inline">type Flavor</code> definition says that &#x201C;a flavor can contain an <code class="md-code md-code-inline">id</code> as an <code class="md-code md-code-inline">ID</code>, a <code class="md-code md-code-inline">name</code> as a <code class="md-code md-code-inline">String</code>, a <code class="md-code md-code-inline">description</code> as a <code class="md-code md-code-inline">String</code>, and <code class="md-code md-code-inline">nutrition</code> as <code class="md-code md-code-inline">Nutrition</code>.&#x201D;</p> <p>For that last one, <code class="md-code md-code-inline">nutrition</code>, we pass the name of <em>another type defined in our <code class="md-code md-code-inline">typeDefs</code></em>. Here, <code class="md-code md-code-inline">type Nutrition</code> describes how nutrition data is shaped in our application.</p> <blockquote> <p>Notice that we&#x2019;re not saying in our database. A database is assumed in the example above, but your data can come from <em>any</em> data source. Even a third-party API or a static file!</p> </blockquote> <p>Just like we did for <code class="md-code md-code-inline">type Flavor</code> we specify the names of the field a piece of <code class="md-code md-code-inline">nutrition</code> data will have, assigning what GraphQL refers to as <em>scalar types</em> to each property. As of writing, GraphQL recognizes <a href="https://graphql.org/learn/schema/#scalar-types" target="_blank" rel="noopener noreferrer">five built-in scalar types</a>:</p> <ul> <li><code class="md-code md-code-inline">Int</code>: A signed 32&#x2010;bit integer.</li> <li><code class="md-code md-code-inline">Float</code>: A signed double-precision floating-point value.</li> <li><code class="md-code md-code-inline">String</code>: A UTF&#x2010;8 character sequence.</li> <li><code class="md-code md-code-inline">Boolean</code>: <code class="md-code md-code-inline">true</code> or <code class="md-code md-code-inline">false</code>.</li> <li><code class="md-code md-code-inline">ID</code>: A unique identifier, often used to refetch an object or as the key for a cache. The ID type is serialized in the same way as a String; however, defining it as an ID signifies that it is not intended to be human&#x2010;readable.</li> </ul> <p>In addition to these scalar types, we can also assign <em>custom types</em> to a property like we did with the <code class="md-code md-code-inline">Nutrition</code> type on the <code class="md-code md-code-inline">nutrition</code> property of the <code class="md-code md-code-inline">type Flavor</code> above.</p> <pre class="md-code-block"><code class="md-code">type Query {
  flavors(id: ID): [Flavor]
}
</code></pre> <p>On our root <code class="md-code md-code-inline">type Query</code> (the &#x201C;root query&#x201D; we talked about earlier), we define the name of a <em>field</em> that can be queried. When defining that field, too, we specify any arguments we might expect along with the type of data we expect to be returned.</p> <p>In this example, we expect a possible <code class="md-code md-code-inline">id</code> argument to be passed as an <code class="md-code md-code-inline">ID</code> scalar type and expect an array of objects resembling the <code class="md-code md-code-inline">Flavor</code> type in response.</p> <h3 id="wiring-up-a-query-resolver">Wiring up a query resolver</h3> <p>With our <code class="md-code md-code-inline">flavors</code> field defined on our root <code class="md-code md-code-inline">type Query</code>, next, we define what&#x2019;s known as a <em>resolver function</em>.</p> <p>This is where GraphQL more or less &#x201C;stops.&#x201D; If we look in the <code class="md-code md-code-inline">resolvers</code> object of our schema file and then in the <code class="md-code md-code-inline">Query</code> object nested under that, we can see a property <code class="md-code md-code-inline">flavors</code> assigned to a function. This <code class="md-code md-code-inline">flavors</code> here is the <em>resolver function</em> for the <code class="md-code md-code-inline">flavors</code> field defined on our root <code class="md-code md-code-inline">type Query</code>.</p> <pre class="md-code-block"><code class="md-code">typeDefs: gql`&#x2026;`,
resolvers: {
  Query: {
    flavors: (parent, args) =&gt; {
      // Assuming args equals an object like { id: &apos;1&apos; }
      return mongodb.collection(&apos;flavors&apos;).find(args).toArray()
    },
  },
  &#x2026;
},
</code></pre> <p>This resolver function takes a few different arguments: the <code class="md-code md-code-inline">parent</code> query if one exists, the <code class="md-code md-code-inline">args</code> passed to the query if any exist, and a missing <code class="md-code md-code-inline">context</code> argument which gives us miscellaneous &#x201C;context&#x201D; data (e.g., the current user if we provide them when our server starts).</p> <p>Inside our resolver, we do <em>whatever we need to do to resolve the query</em>. This is where GraphQL &#x201C;quits caring&#x201D; and leaves retrieving and returning data up to us. Again, this could be a call to a database, an API, a static file&#x2026;<em>anything</em>.</p> <blockquote> <p>While GraphQL doesn&#x2019;t care where our data comes from, it _does _ care about what we return. We can return a JSON object, an array of JSON objects, or a Promise (which GraphQL will resolve for us).</p> </blockquote> <p>Here, we use a mock call to a MongoDB database collection called <code class="md-code md-code-inline">flavors</code>, passing in our <code class="md-code md-code-inline">args</code> (if any exist) to a <code class="md-code md-code-inline">.find()</code> call and returning what it finds as an array.</p> <h3 id="resolving-nested-fields">Resolving nested fields</h3> <p>What may not be clear above is how our nested <code class="md-code md-code-inline">nutrition</code> data is resolved. Remember: we&#x2019;re not actually storing the nutrition data <em>on</em> each <code class="md-code md-code-inline">flavor</code> but assume it lives in another database collection/table.</p> <p>While we <em>did</em> tell GraphQL that our <code class="md-code md-code-inline">type Flavor</code> might include some <code class="md-code md-code-inline">nutrition</code> data in the shape of the <code class="md-code md-code-inline">type Nutrition</code>, we didn&#x2019;t explain how to actually <em>resolve</em> that data. Again: <strong>the <code class="md-code md-code-inline">nutrition</code> data for a flavor is assumed to be in a different collection than the flavor data</strong>.</p> <pre class="md-code-block"><code class="md-code">  typeDefs: gql`
    type Nutrition {
      flavorId: ID
      calories: Int
      fat: Int
      sodium: Int
    }

    type Flavor {
      [&#x2026;]
      nutrition: Nutrition
    }

    type Query {&#x2026;}

    type Mutation {&#x2026;}
  `,
  resolvers: {
    Query: {
      flavors: (parent, args) =&gt; {&#x2026;},
    },
    Mutation: {&#x2026;},
    Flavor: {
      nutrition: (parent) =&gt; {
        return mongodb.collection(&apos;nutrition&apos;).findOne({
          flavorId: parent.id,
        })
      }
    },
  },
</code></pre> <p>If we look close at the <code class="md-code md-code-inline">resolvers</code> object on our schema, notice that we have <code class="md-code md-code-inline">Query</code>, <code class="md-code md-code-inline">Mutation</code>, and <code class="md-code md-code-inline">Flavor</code>. These correspond to the types we defined in the <code class="md-code md-code-inline">typeDefs</code> above.</p> <p>Looking at the <code class="md-code md-code-inline">Flavor</code> object, we can see the field <code class="md-code md-code-inline">nutrition</code> being defined as a <em>resolver function</em>. What&#x2019;s unique about this is that we&#x2019;re defining this _on the <code class="md-code md-code-inline">Flavor</code> type directly. In other words, we&#x2019;re saying &#x201C;this is how we want you to resolve the <code class="md-code md-code-inline">nutrition</code> field for any queries utilizing the <code class="md-code md-code-inline">type Flavor</code>.&#x201D;</p> <p>Inside, we do a familiar MongoDB query, but notice that we utilize the <code class="md-code md-code-inline">parent</code> argument passed to the resolver function. The <code class="md-code md-code-inline">parent</code> here is each iteration of the <code class="md-code md-code-inline">flavors</code> field. For example, if we ask for all flavors at once like this:</p> <pre class="md-code-block"><code class="md-code">{
  flavors {
    id
    name
    nutrition {
      calories
    }
  }
}
</code></pre> <p>For each <code class="md-code md-code-inline">flavor</code> returned by <code class="md-code md-code-inline">flavors</code>, we&#x2019;d pass it through the <code class="md-code md-code-inline">nutrition</code> resolver defined on <code class="md-code md-code-inline">resolvers.Flavor</code> as <code class="md-code md-code-inline">parent</code>. If we look close, we can see that we utilize the <code class="md-code md-code-inline">parent.id</code> field, referring to the <code class="md-code md-code-inline">id</code> of the flavor we&#x2019;re currently iterating (looping) over.</p> <p>We pass that <code class="md-code md-code-inline">parent.id</code> to our database query, matching it to a (presumed) <code class="md-code md-code-inline">flavorId</code> property on each <code class="md-code md-code-inline">nutrition</code> item.</p> <h3 id="wiring-up-mutations">Wiring up mutations</h3> <p>Conveniently, our knowledge of wiring up queries maps over perfectly to mutations. In fact, the process is nearly identical. If we take a look at our root <code class="md-code md-code-inline">type Mutation</code>, we can see that we define a field <code class="md-code md-code-inline">updateFlavor</code> accepting the arguments we specified on the client:</p> <pre class="md-code-block"><code class="md-code">type Mutation {
  updateFlavor(id: ID!, name: String, description: String): Flavor
}
</code></pre> <p>Here, we&#x2019;re saying &#x201C;we expect the <code class="md-code md-code-inline">updateFlavor</code> mutation to accept a possible <code class="md-code md-code-inline">id</code> as an <code class="md-code md-code-inline">ID</code> (the <code class="md-code md-code-inline">!</code> tells GraphQL that this is <em>required</em>), <code class="md-code md-code-inline">name</code> as a <code class="md-code md-code-inline">String</code>, and <code class="md-code md-code-inline">description</code> as a <code class="md-code md-code-inline">String</code>.&#x201D; Additionally, once our mutation completes, we expect some data in return resembling the <code class="md-code md-code-inline">Flavor</code> type (i.e., containing an <code class="md-code md-code-inline">id</code>, <code class="md-code md-code-inline">name</code>, <code class="md-code md-code-inline">description</code>, and/or <code class="md-code md-code-inline">nutrition</code>).</p> <pre class="md-code-block"><code class="md-code">{
  typeDefs: gql`&#x2026;`,
  resolvers: {
    Mutation: {
      updateFlavor: (parent, args) =&gt; {
        // Assuming args equals an object like { id: &apos;1&apos;, name: &apos;Movie Theater Clone&apos;, description: &apos;Bring the movie theater taste home!&apos; }

        // Perform the update.
        mongodb.collection(&apos;flavors&apos;).update(
          { id: args.id },
          {
            $set: {
              ...args,
            },
          },
        )

        // Return the flavor after the update.
        return mongodb.collection(&apos;flavors&apos;).findOne(args.id)
      },
    },
  },
}
</code></pre> <p>Inside our resolver function for the <code class="md-code md-code-inline">updateFlavor</code> mutation, we do what you might expect: interact with our database to change or <em>update</em> the flavor.</p> <p>Notice that immediately after we perform the update, we make a call back to our database to find the same flavor again and return it from our resolver. Why?</p> <p>Remember: on the client, we expect a return value <em>after</em> our mutation completes. In this example, we expect the <code class="md-code md-code-inline">flavor</code> we just updated to be returned.</p> <p>Couldn&#x2019;t we just return the <code class="md-code md-code-inline">args</code> object? Yep! We could. The reason we choose <em>not</em> to in this case is that we want to be 100% certain our database update succeeded. If we go fetch the data again and see that it&#x2019;s changed: all is well!</p> <h1 id="why-would-i-want-to-use-graphql">Why would I want to use GraphQL?</h1> <p>Though it may not look like much, at this point we have a functioning&#x2014;albeit simple&#x2014;GraphQL API up and running!</p> <p>As with any new tech, though, what may not be abundantly clear is why you&#x2019;d even want to use this. To be fair, this <em>is</em> a lot of moving parts. Why shouldn&#x2019;t we just stick to REST or talking to the database directly?</p> <h3 id="you-want-to-reduce-the-number-of-requests-made-from-the-client">You want to reduce the number of requests made from the client</h3> <p>Where a lot of apps get bogged down is in the number, frequency, and complexity of HTTP requests. While GraphQL doesn&#x2019;t completely eliminate requests, utilized properly, it can <em>significantly</em> reduce the number of requests you make from the client (in many cases, down to just one).</p> <p>Whether you&#x2019;re running an app with tons of users or an app with lots of data (e.g., an app for handling medical records), using GraphQL will definitely speed up client performance.</p> <h3 id="you-want-to-avoid-denormalizing-data-just-to-compensate-the-ui">You want to avoid denormalizing data just to compensate the UI</h3> <p>In applications with a lot of relational data, the &#x201C;denormalization trap&#x201D; can be quite common. While this works, it&#x2019;s by no means ideal and can slow things down unnecessarily. With GraphQL and nested queries, the need to denormalize your data is significantly reduced.</p> <h3 id="you-have-multiple-data-sources-to-talk-to-from-different-apps">You have multiple data sources to talk to from different apps</h3> <p>This problem can be solved in part with a traditional REST API, but it still leaves a problem: consistent querying from the client. Assuming you have a product with a web app, iOS app, Android app, and developer API, it&#x2019;s likely that you&#x2019;ll have to rig up querying <em>for each of those platforms</em>.</p> <p>This translates to developing knowledge of multiple client implementations for HTTP requests, an inconsistent means for performing queries, and messy, platform-specific endpoints in your API (don&#x2019;t you &#x201C;holier than thou&#x201D; me, friend, you know you&#x2019;ve done this before!).</p> <h2 id="is-graphql-perfect-should-i-ditch-my-rest-api-today-and-switch-to-it">Is GraphQL perfect? Should I ditch my REST API today and switch to it?</h2> <p>Of course not! <em>Nothing is perfect</em>.</p> <p>Where GraphQL most obviously falls short is its complexity. Writing a GraphQL schema introduces a lot of <em>mandatory</em> steps in order to wire up your data. As you&#x2019;re learning, this can be frustrating because what is missing from your schema may not always be obvious and errors on the client and server may be unhelpful.</p> <p>Further, consuming GraphQL on the client is not standardized beyond the GraphQL query language. Different libraries exists for this, the most popular being Apollo and Relay, each with their own idiosyncrasies.</p> <p>GraphQL is also just a specification. Packages like <code class="md-code md-code-inline">graphql</code> (used internally by the <code class="md-code md-code-inline">express-graphql</code> package from our examples) are just an <em>implementation</em> of that specification. In other words, different implementations for different programming languages may interpret the specification differently. This can lead to problems for you and your team if you use multiple languages across projects.</p> <p>GraphQL is an impressive step forward in handling data, though. It&#x2019;s by no means a silver bullet, but it&#x2019;s certainly worth experimenting with. A great way to get started is to think about a particularly messy part of your data process and try to implement it using GraphQL.</p> <p>This is the great news: GraphQL can be implemented incrementally. <em>You don&#x2019;t have to go all in to start taking advantage of it</em>. This is a great way to get buy in from your team and stakeholders and create an opportunity to get your hands dirty.</p> <p>Keep in mind: GraphQL is ultimately just a tool for doing a job. It&#x2019;s not &#x201C;killing&#x201D; anything. That said, it&#x2019;s worth familiarizing yourself with it and starting to apply in your apps. Anywhere you struggle with performance overhead or complex UIs like data dashboards, news feeds, and user profiles is a great place to get started.</p> <p>Happy coding!</p></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/graphql-in-depth-what-why-and-how</link><guid isPermaLink="true">https://ponyfoo.com/articles/graphql-in-depth-what-why-and-how</guid><category><![CDATA[graphql]]></category><category><![CDATA[apollo]]></category><dc:creator><![CDATA[ryan.glover@cleverbeagle.com (Ryan Glover)]]></dc:creator><pubDate>Tue, 12 Mar 2019 12:01:43 GMT</pubDate></item><item><title><![CDATA[JavaScript Performance Pitfalls in V8]]></title><description><![CDATA[<div class="f-core md-markdown"><p>In recent years, JavaScript engines have improved on all fronts. The performance of JavaScript has reached a level where it can easily compete with programming languages that have traditionally been considered more appropriate for high-performance computing. This is not only true for V8, the JavaScript engine inside of Chrome and Node.js, but for all major JavaScript engines, including ChakraCore, the engine inside of Edge, JavaScriptCore, the engine inside of Safari, and SpiderMonkey, the engine inside of Firefox.</p> <p>Not only did the peak performance improve, but engines also managed to deliver more consistent and predictable levels of performance. Given that JavaScript is a <em>highly dynamic</em> language, all of this performance is based on choosing the right heuristics in the engine. JavaScript engines use techniques like <a href="https://ponyfoo.com/articles/an-introduction-to-speculative-optimization-in-v8" target="_blank" rel="noopener noreferrer">speculative optimization</a> and <a href="https://mathiasbynens.be/notes/shapes-ics" target="_blank" rel="noopener noreferrer">inline caching</a> to speed up execution of the <em>likely path</em>.</p> <p>But heuristics can also easily work against you, and it helps to be aware of them. So today I&#x2019;m gonna share some background on two subtle performance pitfalls in the <a href="https://v8.dev/" target="_blank" rel="noopener noreferrer">V8 JavaScript engine</a>. Being aware of these potential pitfalls might help you to resolve issues if you get bitten by these more easily.</p><h2 id="optimization-limit">Optimization limit</h2> <p>The compilers built into V8 - both the TurboFan optimizing compiler and the Ignition bytecode generator - are so-called <em>method JITs</em>, meaning the unit of compilation is always a method, aka a function in JavaScript speak. The optimizing compiler is able to include the bodies of other methods when it finds hot call sites and sees potential for further optimizations via doing this, which is commonly referred to as <em>inlining</em>. Contrast this with other runtimes that use so-called <a href="https://en.wikipedia.org/wiki/Tracing_just-in-time_compilation" target="_blank" rel="noopener noreferrer"><em>tracing JITs</em></a> - LuaJIT and PyPy are popular examples here - where the unit of optimization is an arbitrary sequence of consecutive bytecodes that have been executed repeatedly previously.</p> <p>In both method JITs as well as tracing JITs, there&#x2019;s always an upper limit on the size of the input that the optimizer can deal with. For method JITs this limit is naturally defined by the size of the function itself. TurboFan also has such a limit, which is currently 60 KiB of bytecodes (the <a href="https://github.com/v8/v8/blob/e16f4a939e41732bb0103c0847b464501cf760b6/src/runtime-profiler.cc#L38-L39" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">kMaxBytecodeSizeForOpt</code> constant</a> in <a href="https://github.com/v8/v8/blob/e16f4a939e41732bb0103c0847b464501cf760b6/src/runtime-profiler.cc" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">runtime-profiler.cc</code></a>). That means if you have functions whose generated bytecode is bigger than 60 KiB, they will not ever be optimized by TurboFan, even if they are considered hot (i.e. invoked very often).</p> <p>Let&#x2019;s consider an example, using <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/eval" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">eval</code></a> to dynamically generate functions. (We intentionally don&#x2019;t use the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Function" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">Function</code> constructor</a> here since that doesn&#x2019;t allow us to put a name onto the function that is syntactically recognizable by the JavaScript engine, and that would make the investigations really challenging).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">generate</span><span class="md-code-params">(n)</span> </span>{
  <span class="md-code-keyword">let</span> s = <span class="md-code-string">&quot;(function add&quot;</span> + n + <span class="md-code-string">&quot;(x) { return 0&quot;</span>;
  <span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; n; ++i) {
    s += <span class="md-code-string">&quot;+x&quot;</span>;
  }
  s += <span class="md-code-string">&quot;; })&quot;</span>;
  <span class="md-code-keyword">return</span> <span class="md-code-built_in">eval</span>(s);
}
</code></pre> <p>This generate function constructs a new JavaScript function object which adds up the parameter passed to it n times. So when you call generate(10) you&#x2019;ll get a function object that looks like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">add10</span><span class="md-code-params">(x)</span> </span>{
  <span class="md-code-keyword">return</span> <span class="md-code-number">0</span>+x+x+x+x+x+x+x+x+x+x;
}
</code></pre> <p>Now let&#x2019;s take a concrete example of the above and run it in <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer">Node.js</a>:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">// add10.js</span>

<span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">generate</span><span class="md-code-params">(n)</span> </span>{
  <span class="md-code-keyword">let</span> s = <span class="md-code-string">&quot;(function add&quot;</span> + n + <span class="md-code-string">&quot;(x) { return 0&quot;</span>;
  <span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; n; ++i) {
    s += <span class="md-code-string">&quot;+x&quot;</span>;
  }
  s += <span class="md-code-string">&quot;; })&quot;</span>;
  <span class="md-code-keyword">return</span> <span class="md-code-built_in">eval</span>(s);
}

<span class="md-code-keyword">const</span> add10 = generate(<span class="md-code-number">10</span>);
add10();
</code></pre> <p>Looking at the generated bytecode for the add10 function, using the <code class="md-code md-code-inline">--print-bytecode</code> flag with the <a href="https://nodejs.org/" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">node</code> shell</a>, we see something like this (output is from Node v10.15):</p> <pre class="md-code-block"><code class="md-code">$ node --print-bytecode add10.js
&#x2026;
[generated bytecode for function: add10]
Parameter count 2
Frame size 8
   19 E&gt; 0x279c523204b2 @    0 : a5                StackCheck
   26 S&gt; 0x279c523204b3 @    1 : 0b                LdaZero
         0x279c523204b4 @    2 : 26 fb             Star r0
         0x279c523204b6 @    4 : 25 02             Ldar a0
   34 E&gt; 0x279c523204b8 @    6 : 34 fb 00          Add r0, [0]
         0x279c523204bb @    9 : 26 fb             Star r0
         0x279c523204bd @   11 : 25 02             Ldar a0
   36 E&gt; 0x279c523204bf @   13 : 34 fb 01          Add r0, [1]
         0x279c523204c2 @   16 : 26 fb             Star r0
         0x279c523204c4 @   18 : 25 02             Ldar a0
   38 E&gt; 0x279c523204c6 @   20 : 34 fb 02          Add r0, [2]
         0x279c523204c9 @   23 : 26 fb             Star r0
         0x279c523204cb @   25 : 25 02             Ldar a0
   40 E&gt; 0x279c523204cd @   27 : 34 fb 03          Add r0, [3]
         0x279c523204d0 @   30 : 26 fb             Star r0
         0x279c523204d2 @   32 : 25 02             Ldar a0
   42 E&gt; 0x279c523204d4 @   34 : 34 fb 04          Add r0, [4]
         0x279c523204d7 @   37 : 26 fb             Star r0
         0x279c523204d9 @   39 : 25 02             Ldar a0
   44 E&gt; 0x279c523204db @   41 : 34 fb 05          Add r0, [5]
         0x279c523204de @   44 : 26 fb             Star r0
         0x279c523204e0 @   46 : 25 02             Ldar a0
   46 E&gt; 0x279c523204e2 @   48 : 34 fb 06          Add r0, [6]
         0x279c523204e5 @   51 : 26 fb             Star r0
         0x279c523204e7 @   53 : 25 02             Ldar a0
   48 E&gt; 0x279c523204e9 @   55 : 34 fb 07          Add r0, [7]
         0x279c523204ec @   58 : 26 fb             Star r0
         0x279c523204ee @   60 : 25 02             Ldar a0
   50 E&gt; 0x279c523204f0 @   62 : 34 fb 08          Add r0, [8]
         0x279c523204f3 @   65 : 26 fb             Star r0
         0x279c523204f5 @   67 : 25 02             Ldar a0
   52 E&gt; 0x279c523204f7 @   69 : 34 fb 09          Add r0, [9]
   55 S&gt; 0x279c523204fa @   72 : a9                Return
Constant pool (size = 0)
Handler Table (size = 0)
&#x2026;
</code></pre> <p>Since Node.js runs a lot of its own JavaScript, you might get pages of output here. Search for the phrase <code class="md-code md-code-inline">[generated bytecode for function: add10]</code> where the following bytecode dump contains a sequence of 10 Adds with Star and Ldar bytecodes in between. There&#x2019;s a blog post on <a href="https://medium.com/dailyjs/understanding-v8s-bytecode-317d46c94775" target="_blank" rel="noopener noreferrer">Understanding V8&#x2019;s Bytecode</a> from my former colleague Franziska Hinkelmann if you&#x2019;re curious to learn more about this topic.</p> <p>Removing the irrelevant parts we see this concrete bytecode output:</p> <pre class="md-code-block"><code class="md-code">  0 : a5                StackCheck
  1 : 0b                LdaZero
  2 : 26 fb             Star r0
  4 : 25 02             Ldar a0
  6 : 34 fb 00          Add r0, [0]
  9 : 26 fb             Star r0
 11 : 25 02             Ldar a0
 13 : 34 fb 01          Add r0, [1]
 16 : 26 fb             Star r0
 18 : 25 02             Ldar a0
 20 : 34 fb 02          Add r0, [2]
 23 : 26 fb             Star r0
 25 : 25 02             Ldar a0
 27 : 34 fb 03          Add r0, [3]
 30 : 26 fb             Star r0
 32 : 25 02             Ldar a0
 34 : 34 fb 04          Add r0, [4]
 37 : 26 fb             Star r0
 39 : 25 02             Ldar a0
 41 : 34 fb 05          Add r0, [5]
 44 : 26 fb             Star r0
 46 : 25 02             Ldar a0
 48 : 34 fb 06          Add r0, [6]
 51 : 26 fb             Star r0
 53 : 25 02             Ldar a0
 55 : 34 fb 07          Add r0, [7]
 58 : 26 fb             Star r0
 60 : 25 02             Ldar a0
 62 : 34 fb 08          Add r0, [8]
 65 : 26 fb             Star r0
 67 : 25 02             Ldar a0
 69 : 34 fb 09          Add r0, [9]
 72 : a9                Return
</code></pre> <p>Looking at the output above, we see that the generated bytecode for <code class="md-code md-code-inline">add10</code> is 73 bytes in size (72 is the offset of the <code class="md-code md-code-inline">Return</code> bytecode and that instruction is 1 byte in size). Given what we learned before about the 60 KiB limit in TurboFan, this function should be easily optimizable by V8. Let&#x2019;s try and see what happens if we put that function in a hot loop.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">// add10-optimized.js</span>

<span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">generate</span><span class="md-code-params">(n)</span> </span>{
  <span class="md-code-keyword">let</span> s = <span class="md-code-string">&quot;(function add&quot;</span> + n + <span class="md-code-string">&quot;(x) { return 0&quot;</span>;
  <span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; n; ++i) {
    s += <span class="md-code-string">&quot;+x&quot;</span>;
  }
  s += <span class="md-code-string">&quot;; })&quot;</span>;
  <span class="md-code-keyword">return</span> <span class="md-code-built_in">eval</span>(s);
}

<span class="md-code-keyword">const</span> add10 = generate(<span class="md-code-number">10</span>);

<span class="md-code-keyword">let</span> result = <span class="md-code-number">0</span>;
<span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; <span class="md-code-number">5</span> * <span class="md-code-number">1000</span>; ++i) {
  result += add10(i);
}
</code></pre> <p>Running this snippet inside of Node, passing the <code class="md-code md-code-inline">--trace-opt</code> command line flag, we see something similar to the following output:</p> <pre class="md-code-block"><code class="md-code md-lang-xml">$ node --trace-opt add10-optimized.js
[marking 0x32fc83347751 <span class="md-code-tag">&lt;<span class="md-code-title">JSFunction</span> <span class="md-code-attribute">add10</span> (<span class="md-code-attribute">sfi</span> = <span class="md-code-attribute">0x32fcde157b91</span>)&gt;</span> for optimized recompilation, reason: small function, ICs with typeinfo: 10/10 (100%), generic ICs: 0/10 (0%)]
[compiling method 0x32fc83347751 <span class="md-code-tag">&lt;<span class="md-code-title">JSFunction</span> <span class="md-code-attribute">add10</span> (<span class="md-code-attribute">sfi</span> = <span class="md-code-attribute">0x32fcde157b91</span>)&gt;</span> using TurboFan]
[optimizing 0x32fc83347751 <span class="md-code-tag">&lt;<span class="md-code-title">JSFunction</span> <span class="md-code-attribute">add10</span> (<span class="md-code-attribute">sfi</span> = <span class="md-code-attribute">0x32fcde157b91</span>)&gt;</span> - took 0.823, 0.514, 0.016 ms]
[completed optimizing 0x32fc83347751 <span class="md-code-tag">&lt;<span class="md-code-title">JSFunction</span> <span class="md-code-attribute">add10</span> (<span class="md-code-attribute">sfi</span> = <span class="md-code-attribute">0x32fcde157b91</span>)&gt;</span>]
</code></pre> <p>You may see messages about other functions being optimized or marked for optimized recompilation as well, just ignore those. The interesting line is the last line, which says that <code class="md-code md-code-inline">add10</code> was successfully optimized by TurboFan. This matches our expectations. Note that every modern JavaScript engine gives you this behavior.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-comment">// add10000.js</span>

<span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">generate</span><span class="md-code-params">(n)</span> </span>{
  <span class="md-code-keyword">let</span> s = <span class="md-code-string">&quot;(function add&quot;</span> + n + <span class="md-code-string">&quot;(x) { return 0&quot;</span>;
  <span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; n; ++i) {
    s += <span class="md-code-string">&quot;+x&quot;</span>;
  }
  s += <span class="md-code-string">&quot;; })&quot;</span>;
  <span class="md-code-keyword">return</span> <span class="md-code-built_in">eval</span>(s);
}

<span class="md-code-keyword">const</span> add10000 = generate(<span class="md-code-number">10</span> * <span class="md-code-number">1000</span>);

<span class="md-code-keyword">let</span> result = <span class="md-code-number">0</span>;
<span class="md-code-keyword">for</span> (<span class="md-code-keyword">let</span> i = <span class="md-code-number">0</span>; i &lt; <span class="md-code-number">5</span> * <span class="md-code-number">1000</span>; ++i) {
  result += add10000(i);
}
</code></pre> <p>Let&#x2019;s see what happens if we increase the size of the generated function such that the bytecode for the function exceeds the limit in TurboFan. We choose a rather arbitrary number of 10,000 additions for this example. Running&#x2026;</p> <pre class="md-code-block"><code class="md-code">node --trace-opt add10000.js
</code></pre> <p>Doesn&#x2019;t print anything to the console, meaning that <code class="md-code md-code-inline">add10000</code> is not even considered for optimization by TurboFan (it&#x2019;s a bit unfortunate that V8 doesn&#x2019;t say anything in this case, but just silently continues). In fact TurboFan is not even involved here! Instead, the so-called <code class="md-code md-code-inline">RuntimeProfiler</code> that profiles bytecode execution immediately decides that the function is too big to be considered for optimization and disables optimization of the function (in the <a href="https://github.com/v8/v8/blob/e16f4a939e41732bb0103c0847b464501cf760b6/src/runtime-profiler.cc#L190-L192" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">RuntimeProfiler::ShouldOptimize</code> method</a>).</p> <p>Running <code class="md-code md-code-inline">node</code> again with <code class="md-code md-code-inline">--print-bytecode</code> we see the culprit:</p> <pre class="md-code-block"><code class="md-code">$ node --print-bytecode add10000.js
&#x2026;
[generated bytecode for function: add10000]
Parameter count 2
Frame size 8
   18 E&gt; 0x2e785b7dcf22 @    0 : a0                StackCheck
   24 S&gt; 0x2e785b7dcf23 @    1 : 0b                LdaZero
         0x2e785b7dcf24 @    2 : 26 fb             Star r0
         0x2e785b7dcf26 @    4 : 25 02             Ldar a0
   32 E&gt; 0x2e785b7dcf28 @    6 : 32 fb 00          Add r0, [0]
         0x2e785b7dcf2b @    9 : 26 fb             Star r0
         0x2e785b7dcf2d @   11 : 25 02             Ldar a0
   34 E&gt; 0x2e785b7dcf2f @   13 : 32 fb 01          Add r0, [1]
&#x2026;
20026 E&gt; 0x2e785b7f52aa @ 99208 : 00 32 fb ff 0d 27 Add.Wide r0, [9997]
         0x2e785b7f52b0 @ 99214 : 26 fb             Star r0
         0x2e785b7f52b2 @ 99216 : 25 02             Ldar a0
20028 E&gt; 0x2e785b7f52b4 @ 99218 : 00 32 fb ff 0e 27 Add.Wide r0, [9998]
         0x2e785b7f52ba @ 99224 : 26 fb             Star r0
         0x2e785b7f52bc @ 99226 : 25 02             Ldar a0
20030 E&gt; 0x2e785b7f52be @ 99228 : 00 32 fb ff 0f 27 Add.Wide r0, [9999]
20033 S&gt; 0x2e785b7f52c4 @ 99234 : a4                Return
Constant pool (size = 0)
Handler Table (size = 0)
</code></pre> <p>The function <code class="md-code md-code-inline">add10000</code> generated <strong>99,235 bytes</strong> of bytecode instructions. That certainly exceeds the <strong>60 KiB</strong> limit imposed by the <code class="md-code md-code-inline">RuntimeProfiler</code>.</p> <h3 id="background">Background</h3> <p>There&#x2019;s an ongoing discussion in <a href="https://crbug.com/v8/8598" target="_blank" rel="noopener noreferrer">v8:8598</a> regarding the issue of this optimization limit. In general, there&#x2019;ll always be a limit of some form, since it&#x2019;s all about trade-offs in the engine. In V8&#x2019;s case, the limit allows TurboFan to store various counts (i.e. the number of inputs and outputs of entities in the intermediate representation) as 16-bit integers vs. 32-bit integers. Other engines and languages have similar limits. For example, in Java the 64 KiB method limit is even part of the <a href="https://docs.oracle.com/javase/specs/jvms/se8/html/jvms-4.html#jvms-4.7.3" target="_blank" rel="noopener noreferrer">JVM bytecode specification</a>.</p> <h3 id="take-away-from-this-section">Take-away from this section</h3> <p>Make sure to split large functions into smaller building blocks. This is generally good advice for maintainability, but also helps the JIT to properly optimize everything that&#x2019;s relevant to your application. Smaller functions also generally play well together with the inlining machinery inside the JIT, and often reduce the cost of compilation and optimization. Imagine you have a bigger function that does 10 different tasks;ven if you only need 1 or 2 of those tasks, you still pay the cost of compiling everything that&#x2019;s in the function (at least compiling to bytecode), plus since inlining heuristics also take into account the size of the inlinee, it&#x2019;s a lot less likely that such a function is inlined at a hot call site.</p> <p>It&#x2019;s worth pointing out that normally you don&#x2019;t run into the 60KiB limit when writing JavaScript, except in some really extreme cases. But tools generating JavaScript programmatically (i.e. parser generators) can hit this limit easily.</p> <h2 id="double-fields">Double fields</h2> <p>The V8 engine uses a technique called <a href="https://en.wikipedia.org/wiki/Tagged_pointer" target="_blank" rel="noopener noreferrer">pointer tagging</a> to encode arbitrary JavaScript values. The trick here to realize that while pointers can be used to address any single byte in memory, this is not needed for JavaScript objects in memory, which usually are aligned to word boundaries (i.e. 4-byte aligned on 32-bit architectures and 8-byte aligned on 64-bit architectures). So the least significant bits in any valid object pointer are zero. V8 uses these bits to encode additional information. In particular it uses the two least significant bits to distinguish between three different kinds of values:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/1-8b65bb067a394418bd9858de58bad17a.svg"></figure> <p>A <code class="md-code md-code-inline">Smi</code> is a small integer in the 31-bit range, i.e. a value between <code class="md-code md-code-inline">-1,073,741,824</code> and <code class="md-code md-code-inline">1,073,741,823</code><sup><a id="footnote-1" href="#footnote-1-marker" class="md-footnote-ref">1</a></sup>, shifted up by one bit and padded with a <code class="md-code md-code-inline">0</code> in the least significant bit. A <code class="md-code md-code-inline">HeapObject</code> pointer is the address of an object in (managed) memory, with the two least significant bits set to <code class="md-code md-code-inline">01</code>. That means when V8 wants to get to the real address of the object it has to subtract one from the value. There&#x2019;s also the <code class="md-code md-code-inline">WeakHeapObject</code>, which has the least significant bits set to <code class="md-code md-code-inline">11</code>, and is essentially like <code class="md-code md-code-inline">HeapObject</code>, except that the reference is treated weakly by the garbage collector.</p> <p>For the purpose of this article we only care about <code class="md-code md-code-inline">Smi</code> and <code class="md-code md-code-inline">HeapObject</code>. Given that small integers are the most common number values in JavaScript programs, it was considered important to have an efficient value encoding for those. That&#x2019;s why there&#x2019;s the special <code class="md-code md-code-inline">Smi</code> encoding: so that small integers can be stored efficiently, for example inside of objects.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> a = {
  x: <span class="md-code-number">42</span>,
  y: <span class="md-code-string">&quot;Hello&quot;</span>
};
</code></pre> <p>Just consider the object <code class="md-code md-code-inline">a</code> above. It has two properties <code class="md-code md-code-inline">x</code> and <code class="md-code md-code-inline">y</code>, and <code class="md-code md-code-inline">x</code> has a small integer value, whereas <code class="md-code md-code-inline">y</code> has a <code class="md-code md-code-inline">String</code> value. V8 is going to represent that object in memory like this:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/2-81b41e764823440892cc5e2d335d69c9.svg"></figure> <p>Here <code class="md-code md-code-inline">a</code> is represented as a <code class="md-code md-code-inline">JSObject</code> with a <code class="md-code md-code-inline">Shape</code> that holds the information about the properties of <code class="md-code md-code-inline">a</code>, and the actual values of the properties are in the instance <code class="md-code md-code-inline">a</code> (you can read more about shapes in our previous articles <a href="https://mathiasbynens.be/notes/shapes-ics" target="_blank" rel="noopener noreferrer">JavaScript engine fundamentals: Shapes and Inline Caches</a> and <a href="https://mathiasbynens.be/notes/prototypes" target="_blank" rel="noopener noreferrer">JavaScript engine fundamentals: optimizing prototypes</a>). Due to the value encoding mentioned above, the small integer <code class="md-code md-code-inline">42</code> can be stored efficiently inside of the <code class="md-code md-code-inline">JSObject</code> whereas the <code class="md-code md-code-inline">String</code> value has to be represented as a separate entity in memory and pointed to by a <code class="md-code md-code-inline">HeapObject</code> pointer.</p> <p>Now for <code class="md-code md-code-inline">String</code>s that seems kind of obvious and makes sense to have them allocated as separate entities<sup><a id="footnote-2" href="#footnote-2-marker" class="md-footnote-ref">2</a></sup>, but what about other number values for example? In JavaScript numbers are <a href="https://en.wikipedia.org/wiki/Double-precision_floating-point_format" target="_blank" rel="noopener noreferrer">64-bit double precision floating point values</a> so they can encode a lot of values outside the 31-bit integer range. In the basic pointer tagging scheme used in V8, all numbers outside this range have to represented as separately allocated <code class="md-code md-code-inline">HeapObject</code> entities.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> b = {
  x: <span class="md-code-number">2</span> ** <span class="md-code-number">32</span>,
  y: <span class="md-code-number">1.5</span>
};
</code></pre> <p>In the example of <code class="md-code md-code-inline">b</code> above, we assign <code class="md-code md-code-inline">x</code> an integer value 4,294,967,296, which is outside the 31-bit integer range, and <code class="md-code md-code-inline">y</code> is assigned a number that is not even an integer at all. Neither of these values cannot use the efficient <code class="md-code md-code-inline">Smi</code> encoding, and thus need separate heap-allocated entities, so-called <code class="md-code md-code-inline">HeapNumber</code>s in V8 speak:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/3-7f6a7c8cb77042f0a5259b7d6f5ce065.svg"></figure> <p>As you can imagine this would be quite costly when you think of applications that operate on data structures that mostly consist of numeric values outside the 31-bit signed integer range, i.e. points, vectors, etc, and often update the values of these properties. In such cases every update to these properties would have to allocate a new <code class="md-code md-code-inline">HeapNumber</code> entity, since these entities are by their nature immutable (since they are passed around and thus can be referenced from many different places).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> c = {x: <span class="md-code-number">1.1</span>};
c.x = <span class="md-code-number">1.2</span>;
c.x = <span class="md-code-number">1.3</span>;
</code></pre> <p>Running this simple snippet results in the creation of three <code class="md-code md-code-inline">HeapNumber</code> objects, one for each of the different numbers <code class="md-code md-code-inline">1.1</code>, <code class="md-code md-code-inline">1.2</code> and <code class="md-code md-code-inline">1.3</code>.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/4-ae3d54be43c34f698f379903b82574de.svg"></figure> <p>Always having to allocate new <code class="md-code md-code-inline">HeapNumber</code>s can be quite costly specifically for numeric applications, as it causes a lot of traffic on the garbage collector and may lead to bad cache locality.</p> <p>To mitigate this problem for applications that operate on lots of objects with numeric properties, V8 has a concept called <em>field representation tracking</em>, which tracks for each field in an object what the <em>representation</em> of the values were so far. Specifically V8 tracks whether a field (as described by a concrete shape) has always stored numbers so far, where we distinguish between states <code class="md-code md-code-inline">Smi</code> and <code class="md-code md-code-inline">Double</code> (the former meaning that only values in the small integer range were seen, the latter means that also other number values outside that range occurred).</p> <p>Fields with <code class="md-code md-code-inline">Smi</code> representation are already stored efficiently as described above, but the optimizing compiler TurboFan can use the field representation information to generate more efficient code (i.e. it doesn&#x2019;t need to check whether the field contains an integer since that&#x2019;s known from the representation on the shape). For <code class="md-code md-code-inline">Double</code> fields a new <code class="md-code md-code-inline">MutableHeapNumber</code> entity was introduced that looks like a <code class="md-code md-code-inline">HeapNumber</code>, but is tied to the instance and can be updated in-place.</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> c = {x: <span class="md-code-number">1.1</span>};
c.x = <span class="md-code-number">1.2</span>;
c.x = <span class="md-code-number">1.3</span>;
</code></pre> <p>Looking at this example with the update again, we see that only a single storage for the double values need to be allocated and it&#x2019;s just updated to the most recent value in-place:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/5-5267ccde251b4efb98739191cf42becd.svg"></figure> <p>Notice how the <em>Property information</em> now tags the property <code class="md-code md-code-inline">&apos;x&apos;</code> as a <code class="md-code md-code-inline">Double</code> field.</p> <p>This optimization is done by V8 under the hood, as a developer you don&#x2019;t even notice that it&#x2019;s going on. And even better, on 64-bit architectures V8 can store the double values inline into the instance itself, using a technique called <em>double field unboxing</em>. I&#x2019;m not going to describe that here, since the details of that are quite complex. Take-away so far should be that this goes on automatically and usually does the right thing for you.</p> <p>But as with any heuristic, there are trade-offs. You might already ask yourself how that work with the dynamic nature of JavaScript? Specifically since a field can hold any arbitrary value at any given point in time. Like what if you decide to store a string into <code class="md-code md-code-inline">c.x</code> at some point?</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">c.x = <span class="md-code-string">&quot;Hello&quot;</span>;
</code></pre> <p>This is perfectly valid in JavaScript, and V8 handles this by having a lattice of field representations like this:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/6-8c0e7f9ab9234027a0de90890a49bf86.svg"></figure> <p>For each field V8 will choose the best possible representation given the value that&#x2019;s being stored, and it will progress through the lattice as necessary. Like in the case of storing a string into a field that currently has a <code class="md-code md-code-inline">Double</code> representation tag, it&#x2019;ll progress the field to <code class="md-code md-code-inline">Tagged</code> representation, which essentially says <em>anything</em>. Ideally fields should have one of <code class="md-code md-code-inline">Smi</code>, <code class="md-code md-code-inline">Double</code> or <code class="md-code md-code-inline">HeapObject</code> representation tags (highlighted in orange above), i.e. you should not mix numbers and other values, for best performance in most cases. You may already know about <a href="https://v8.dev/blog/elements-kinds" target="_blank" rel="noopener noreferrer">the concept of elements kinds in V8</a>, which is very similar to the field representation mechanism. Elements kinds are concerned with array-indexed properties, whereas field representations are used for non-array indexed properties.</p> <h3 id="mixed-number-and-non-number-fields">Mixed number and non-number fields</h3> <p>When you store both number and non-number values into a field, its field representation is going to be <code class="md-code md-code-inline">Tagged</code> for sure, since that&#x2019;s the only valid representation for fields that hold both number and non-number values. If this is really what you intended to do, good. But if you intended to operate on number values really, then mixing in non-number values, including <code class="md-code md-code-inline">undefined</code> or <code class="md-code md-code-inline">null</code> primitives is a bad idea. Performance-wise the JavaScript engine can skip a bunch of checks if it knows that a field has <code class="md-code md-code-inline">Smi</code> or <code class="md-code md-code-inline">Double</code> representation. Consider this simple example</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-function"><span class="md-code-keyword">function</span> <span class="md-code-title">distance</span><span class="md-code-params">(p1, p2)</span> </span>{
  <span class="md-code-keyword">const</span> dx = p1.x - p2.x;
  <span class="md-code-keyword">const</span> dy = p1.y - p2.y;
  <span class="md-code-keyword">return</span> <span class="md-code-built_in">Math</span>.sqrt(dx * dx + dy * dy);
}
</code></pre> <p>which computes the distance between two points <code class="md-code md-code-inline">p1</code> and <code class="md-code md-code-inline">p2</code>. Without any additional information about the representation of the fields <code class="md-code md-code-inline">x</code> and <code class="md-code md-code-inline">y</code>, the engine would need to check that the results of the four property accesses <code class="md-code md-code-inline">p1.x</code>, <code class="md-code md-code-inline">p2.x</code>, <code class="md-code md-code-inline">p1.y</code>, and <code class="md-code md-code-inline">p2.y</code> all yields numbers (and handle the case where they are not).</p> <p>Specifically don&#x2019;t ever pre-initialize number fields with <code class="md-code md-code-inline">undefined</code> or <code class="md-code md-code-inline">null</code>, unless there&#x2019;s a good reason why this would be necessary for your application. That being said, don&#x2019;t do something like this</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">class</span> Point {
  constructor() {
    <span class="md-code-keyword">this</span>.x = <span class="md-code-literal">null</span>;
    <span class="md-code-keyword">this</span>.y = <span class="md-code-literal">null</span>;
  }

  setX(x) { <span class="md-code-keyword">this</span>.x = x; }
  setY(y) { <span class="md-code-keyword">this</span>.y = y; }
}
</code></pre> <p>i.e. use <code class="md-code md-code-inline">null</code> as a sentinel value for number fields. Prefer actual number values, i.e. maybe just <code class="md-code md-code-inline">0</code> (if you plan on using small integers) or <code class="md-code md-code-inline">0.1</code> or even <code class="md-code md-code-inline">NaN</code> (if you plan on using floats).</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">class</span> Point {
  constructor() {
    <span class="md-code-keyword">this</span>.x = <span class="md-code-number">0.1</span>;
    <span class="md-code-keyword">this</span>.y = <span class="md-code-number">0.1</span>;
  }

  setX(x) { <span class="md-code-keyword">this</span>.x = x; }
  setY(y) { <span class="md-code-keyword">this</span>.y = y; }
}
</code></pre> <h3 id="smi-to-double-migrations"><code class="md-code md-code-inline">Smi</code> to <code class="md-code md-code-inline">Double</code> migrations</h3> <p>Even if you pay attention to what was said above, there&#x2019;s still another subtle problem that you might run into with number fields. For example the <a href="https://github.com/facebook/react" target="_blank" rel="noopener noreferrer">React</a> team got bitten by this <a href="https://github.com/facebook/react/issues/14365" target="_blank" rel="noopener noreferrer">recently</a>. The subtle issue here, without going into too much detail, is that <code class="md-code md-code-inline">Smi</code> and <code class="md-code md-code-inline">Double</code> field representation aren&#x2019;t compatible: The former requires the number to be encoded as part of the value in the field, the latter requires the number to be stored in a dedicated <code class="md-code md-code-inline">MutableHeapNumber</code> entity. So going from <code class="md-code md-code-inline">Smi</code> to <code class="md-code md-code-inline">Double</code> involves a process called <em>instance migration</em>, where instances that used to have the value stored as a <code class="md-code md-code-inline">Smi</code> need to be migrated to have the field value stored in a <code class="md-code md-code-inline">MutableHeapNumber</code> instead.</p> <p>This process involves creating entirely new shapes for the instances, and lazily migrating instances from the old shape to the new shape as the engine hits the instances. (It would be too expensive to stop the world and find all the instances in managed memory at the time when the migration is kicked off). Since shapes are organized in trees (as explained in <a href="https://mathiasbynens.be/notes/shapes-ics" target="_blank" rel="noopener noreferrer">JavaScript engine fundamentals: Shapes and Inline Caches</a>), this might involve the creation of new shape subtrees. Consider this example of two objects <code class="md-code md-code-inline">o1</code> and <code class="md-code md-code-inline">o2</code> with <code class="md-code md-code-inline">Smi</code> fields <code class="md-code md-code-inline">x</code> and <code class="md-code md-code-inline">y</code>:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">const</span> o1 = {};
o1.x = <span class="md-code-number">3</span>;
o1.y = <span class="md-code-number">4</span>;

<span class="md-code-keyword">const</span> o2 = {};
o2.x = <span class="md-code-number">5</span>;
o2.y = <span class="md-code-number">6</span>;
</code></pre> <p>This creates appropriate shapes and transitions between these shapes as outlined below:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/7-86e5543b40894a178742aaa94ca0d7a2.svg"></figure> <p>Now imagine we change the value of <code class="md-code md-code-inline">o2.x</code> to value <code class="md-code md-code-inline">5.5</code>, which cannot be represented as a <code class="md-code md-code-inline">Smi</code> like this:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript">o2.x = <span class="md-code-number">5.5</span>;
</code></pre> <p>Now V8 has to create a whole new subtree starting from the shape that introduced the property <code class="md-code md-code-inline">x</code>, and have <code class="md-code md-code-inline">o2</code> use the new shape. The instance <code class="md-code md-code-inline">o1</code> still points to the previous shape, which is now marked as deprecated.</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/8-b321185c82e8443a919ebc598a320f69.svg"></figure> <p>As the diagram shows, the old shape subtree is essentially disconnected from the root shape, and instead a new shape subtree is put in place. All the shapes in the old subtree are marked as deprecated, and will eventually be garbage-collected once no more instances use them. In the meantime however there&#x2019;s additional overhead essentially keeping two separate trees of metadata alive.</p> <p>The next time <code class="md-code md-code-inline">o1</code> is seen by any IC (inline cache), it automatically migrates the instance away from the deprecated shape to its so-called <em>migration target</em>, which is the corresponding shape in the new subtree. For example, if you now perform a property access like <code class="md-code md-code-inline">o1.y</code> it&#x2019;ll automatically do this self-healing. You don&#x2019;t even need to store to it; loading any property from it is sufficient. And this also forces <code class="md-code md-code-inline">o1.x</code> into a <code class="md-code md-code-inline">Double</code> field representation, so that afterwards <code class="md-code md-code-inline">o1</code> and <code class="md-code md-code-inline">o2</code> use the same shape again:</p> <figure><img alt class src="https://images.ponyfoo.com/uploads/9-558e060a3d2f4bb19c74b871d2315911.svg"></figure> <p>Now imagine what happens when this has to be done for thousands of objects with plenty of properties on them, which is kinda what happened in <a href="https://github.com/facebook/react/issues/14365" target="_blank" rel="noopener noreferrer">facebook/react#14365</a>, except that in their case, it was some bad interaction with <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/preventExtensions" target="_blank" rel="noopener noreferrer"><code class="md-code md-code-inline">Object.preventExtensions()</code></a> on top, where a bug in V8 causes it to miss the proper <em>migration target</em> somehow, and so V8 ended up allocating a new unique shape for each and every <code class="md-code md-code-inline">FiberNode</code> instance in this application. The relevant issue <a href="https://crbug.com/v8/8538" target="_blank" rel="noopener noreferrer">v8:8538</a> is still under investigation.</p> <p>The actionable advice for you here is to make sure that whenever you&#x2019;re going to use a field to hold arbitrary number values (including values outside the small integer range), initialize this field with a non-small integer number. That way, you get the correct shapes from the beginning, and you avoid the migration cost (and troubles). One easy way to accomplish this is to store <code class="md-code md-code-inline">NaN</code> initially<sup><a id="footnote-3" href="#footnote-3-marker" class="md-footnote-ref">3</a></sup>, which was also the fix for the React issue:</p> <pre class="md-code-block"><code class="md-code md-lang-javascript"><span class="md-code-keyword">class</span> Point {
  constructor(x, y) {
    <span class="md-code-keyword">this</span>.x = <span class="md-code-literal">NaN</span>; <span class="md-code-keyword">this</span>.x = x;
    <span class="md-code-keyword">this</span>.y = <span class="md-code-literal">NaN</span>; <span class="md-code-keyword">this</span>.y = y;
  }
}
</code></pre> <p>But also like with all performance advice: Don&#x2019;t do this blindly everywhere. A lot of your code is probably fine and unaffected performance-wise, even if objects undergo migrations sometimes.</p> <h3 id="take-away-from-this-section">Take-away from this section</h3> <p>Try to avoid mixing field values of different types, i.e. don&#x2019;t mix numbers, strings, objects, other primitives, unless that&#x2019;s what you intended to do. Specifically don&#x2019;t pre-initialize number fields to <code class="md-code md-code-inline">null</code> or <code class="md-code md-code-inline">undefined</code>, but choose sensible default numbers (use <code class="md-code md-code-inline">NaN</code> if in doubt). And try to initialize double fields (aka fields that are supposed to hold number values outside the small integer range) with double values outside the small integer range up-front, i.e. if in doubt put a <code class="md-code md-code-inline">NaN</code> there first, and then store the actual initial value.</p> <h2 id="conclusion">Conclusion</h2> <p>You should trust that the JavaScript engines usually do the right thing for you automatically. But as with everything, it&#x2019;s good to be aware of some of the details, so in case you bump into problems, you&#x2019;ll find your way around it, and sometimes giving the JavaScript engine an additional hint can be beneficial if the heuristics would otherwise fail badly.</p> <div class="md-footnotes"> <div id="footnote-1-marker" class="md-footnote"><a href="#footnote-1" class="md-footnote-anchor">1</a><div><p>At the time of this writing, <code class="md-code md-code-inline">Smi</code>s on 64-bit architectures are integers in the 32-bit range, but this is probably going to change to use 31-bit ranged integers everywhere in the near future.</p> </div></div> <div id="footnote-2-marker" class="md-footnote"><a href="#footnote-2" class="md-footnote-anchor">2</a><div><p>Although there are also JavaScript VMs that manage to store short strings encoded into the value itself, for example the <a href="https://github.com/cesanta/v7" target="_blank" rel="noopener noreferrer">V7 Embedded JavaScript engine</a> does that.</p> </div></div> <div id="footnote-3-marker" class="md-footnote"><a href="#footnote-3" class="md-footnote-anchor">3</a><div><p>Choice of <code class="md-code md-code-inline">NaN</code> is completely arbitrary here, it&#x2019;s just the way I&#x2019;d do it, as it signals to my future self reading this code that I didn&#x2019;t accidentally overwrite the value immediately, but did this on purpose. You can use any non-small integer number here, i.e. <code class="md-code md-code-inline">0.1</code>, <code class="md-code md-code-inline">Number.MAX_VALUE</code> or even <code class="md-code md-code-inline">-0</code> would all work here.</p> </div></div> </div></div><style>html{line-height:1.15;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%}article,aside,details,figcaption,figure,footer,header,main,menu,nav,section{display:block}h1{font-size:2em;margin:.67em 0}figure{margin:1em 40px}hr{box-sizing:content-box;height:0;overflow:visible}code,kbd,pre,samp{font-family:monospace,monospace;font-size:1em}a{background-color:transparent;-webkit-text-decoration-skip:objects}abbr[title]{border-bottom:none;text-decoration:underline;text-decoration:underline dotted}b,strong{font-weight:bolder}dfn{font-style:italic}mark{background-color:#ff0;color:#000}small{font-size:80%}sub,sup{font-size:75%;position:relative;vertical-align:baseline}sup{top:-.5em}audio,canvas,video{display:inline-block}audio:not([controls]){display:none;height:0}svg:not(:root){overflow:hidden}button,input,optgroup,select,textarea{font-family:sans-serif;font-size:100%;line-height:1.15;margin:0}button,select{text-transform:none}[type=reset],[type=submit],button,html [type=button]{-webkit-appearance:button}[type=button]::-moz-focus-inner,[type=reset]::-moz-focus-inner,[type=submit]::-moz-focus-inner,button::-moz-focus-inner{border-style:none;padding:0}[type=button]:-moz-focusring,[type=reset]:-moz-focusring,[type=submit]:-moz-focusring,button:-moz-focusring{outline:ButtonText dotted 1px}fieldset{padding:.35em .75em .625em}legend{box-sizing:border-box;color:inherit;display:table;max-width:100%;padding:0;white-space:normal}progress{display:inline-block;vertical-align:baseline}textarea{overflow:auto;resize:vertical;min-height:280px;max-height:900px}[type=checkbox],[type=radio]{box-sizing:border-box;padding:0}[type=number]::-webkit-inner-spin-button,[type=number]::-webkit-outer-spin-button{height:auto}[type=search]{-webkit-appearance:textfield;outline-offset:-2px}[type=search]::-webkit-search-cancel-button,[type=search]::-webkit-search-decoration{-webkit-appearance:none}::-webkit-file-upload-button{-webkit-appearance:button;font:inherit}summary{display:list-item;margin:-10px -20px;padding:10px;background-color:#f6f6f6;cursor:pointer;border-bottom:2px solid #ddd}[hidden],template{display:none}body,body:after,body:before,html,html:after,html:before{-webkit-box-sizing:border-box;-moz-box-sizing:border-box;box-sizing:border-box}*,:after,:before{-webkit-box-sizing:inherit;-moz-box-sizing:inherit;box-sizing:inherit}@-moz-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-webkit-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-o-keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@keyframes bg-rainbow{0%{background-color:#e92c6c}50%{background-color:#f1e05a}90%{opacity:.6;background-color:#37ed2c}}@-moz-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-webkit-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@-o-keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}@keyframes sb-pulse{0%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,.5);box-shadow:0 0 0 0 rgba(233,44,108,.5)}70%{-webkit-box-shadow:0 0 0 15px rgba(233,44,108,0);box-shadow:0 0 0 15px rgba(233,44,108,0)}100%{-webkit-box-shadow:0 0 0 0 rgba(233,44,108,0);box-shadow:0 0 0 0 rgba(233,44,108,0)}}body{margin:0;font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif;background:#fcfcfc;color:#333}img{max-width:100%;border:none}.md-markdown figure:not(.twitter-tweet-figure){background-color:#f6f6f6}.md-markdown figure:not(.twitter-tweet-figure)>a{display:block}.md-markdown figure:not(.twitter-tweet-figure)>a>img,.md-markdown figure:not(.twitter-tweet-figure)>img{display:block;margin-left:auto;margin-right:auto}.md-markdown figcaption{padding:10px;font-style:italic;font-size:14px;background:-webkit-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-moz-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-o-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:-ms-linear-gradient(left,#fff9e2 33%,#fcf9ee 100%);background:linear-gradient(to right,#fff9e2 33%,#fcf9ee 100%);color:#555}.md-markdown .figure-has-loaded{background:-webkit-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-moz-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-o-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:-ms-linear-gradient(top,rgba(255,255,255,0) 0,#fcf9ee 100%);background:linear-gradient(to bottom,rgba(255,255,255,0) 0,#fcf9ee 100%)}b,em,font-style italic,font-weight bold,i,ins,strong{text-decoration:none}del{text-decoration:line-through}sub,sup{bottom:0;line-height:18px}button,input{overflow:visible;border-radius:0}button{border:none;padding:6px 8px;font-size:1.2em;background:0 0}button[disabled]{color:#cbccbc!important}input,select,textarea{display:block;width:100%;padding:10px 8px;line-height:18px;border:none;font:inherit}select{height:38px}ol,ul{padding:0;margin:0;list-style-position:inside}li>ol,li>ul{margin-left:20px}hr{border:none;border-top:5px solid rgba(0,0,0,.05);margin:20px 0}kbd{display:inline-block;border:1px solid #ccc;margin:0 .1em;padding:.1em .6em;line-height:1.4;font-size:11px;background-color:#f7f7f7;-webkit-box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;box-shadow:0 1px 0 #bbb,0 0 0 2px #fff inset;border-radius:3px;color:#333;text-shadow:0 1px 0 #fff;white-space:nowrap}iframe{display:inline-block;border:none;max-width:100%}summary:hover{opacity:.8}details{padding:10px 20px 20px;border-bottom:2px dashed #ddd}details[open] summary{margin-bottom:20px}blockquote{border:none}ol,p,ul{line-height:35px}p{margin:35px 0}@media only screen and (min-width:768px){ol,p,ul{line-height:30px}}a,button,input[type=submit]{cursor:pointer;text-decoration:none}.lk-link{color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.lk-link .md-code-inline{position:relative}.lk-link .md-code-inline:only-child{display:inline-block;line-height:17px}.lk-link .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.lk-link:visited{background-color:rgba(233,44,108,.04)}.lk-link:visited .md-code-inline:before{background-color:#e92c6c}.lk-link:hover{border-bottom:1px solid #e92c6c}.lk-rainbows{position:relative;color:#333;margin-bottom:5px}.lk-rainbows:hover:before{position:absolute;content:'';left:0;right:0;bottom:-8px;height:3px;opacity:.8;-webkit-transition:.1s ease-in-out;-moz-transition:.1s ease-in-out;-o-transition:.1s ease-in-out;-ms-transition:all .1s ease-in-out;transition:.1s ease-in-out;-webkit-animation:.5s infinite bg-rainbow;-moz-animation:.5s infinite bg-rainbow;-o-animation:.5s infinite bg-rainbow;-ms-animation:bg-rainbow .5s infinite;animation:.5s infinite bg-rainbow}.lk-icon{color:#333}.lk-icon:hover{color:#e92c6c}.lk-visitor{color:#333}.lk-visitor:after{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-left:5px}.lk-visitor.lk-visitor-large:after{font-size:1em}.lk-visitor:hover:after{color:#333;border-bottom-style:solid}.lk-visitor:visited:after{color:#7d5cff}.lk-visitor-inside{color:#333}.lk-visitor-inside .lk-visitor-target:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-inside.lk-visitor-large .lk-visitor-target:before{font-size:1em}.lk-visitor-inside:hover .lk-visitor-target:before{color:#333;border-bottom-style:solid}.lk-visitor-inside:visited .lk-visitor-target:before{color:#7d5cff}.lk-visitor-before{color:#333}.lk-visitor-before:before{display:inline-block;font:.7em/1 FontAwesome;text-rendering:auto;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;content:'\f00c';border-bottom:1px dotted #cbccbc;color:#cbccbc;margin-right:5px}.lk-visitor-before.lk-visitor-large:before{font-size:1em}.lk-visitor-before:hover:before{color:#333;border-bottom-style:solid}.lk-visitor-before:visited:before{color:#7d5cff}.lk-visitor-before-no-underline:before{border-bottom-width:0}.lk-link-to-green{color:#cbccbc}.lk-link-to-green:visited,.lk-link-to-green:visited:before{color:#1bc211;background-color:#1bc211}.lk-link-to-green:active{outline-color:#1bc211}.lk-link-to-green:hover,.lk-link-to-green:hover:before{color:#8be186;border-bottom-color:#1bc211}.lk-green .lk-link,a.lk-green{color:#1bc211}.lk-green .lk-link:visited,a.lk-green:visited{background-color:rgba(27,194,17,.04)}.lk-green .lk-link:visited .md-code-inline:before,a.lk-green:visited .md-code-inline:before{background-color:#1bc211}.lk-green .lk-link:active,a.lk-green:active{outline-color:#1bc211}.lk-green .lk-link:hover,a.lk-green:hover{border-bottom-color:#1bc211}.lk-green.lk-icon:hover{color:#1bc211}.lk-pink-light .lk-link,a.lk-pink-light{color:#ea6c93}.lk-pink-light .lk-link:visited,a.lk-pink-light:visited{background-color:rgba(234,108,147,.04)}.lk-pink-light .lk-link:visited .md-code-inline:before,a.lk-pink-light:visited .md-code-inline:before{background-color:#ea6c93}.lk-pink-light .lk-link:active,a.lk-pink-light:active{outline-color:#ea6c93}.lk-pink-light .lk-link:hover,a.lk-pink-light:hover{border-bottom-color:#ea6c93}.lk-pink-light.lk-icon:hover{color:#ea6c93}.lk-black .lk-link,a.lk-black{color:#333}.lk-black .lk-link:visited,a.lk-black:visited{background-color:rgba(51,51,51,.04)}.lk-black .lk-link:visited .md-code-inline:before,a.lk-black:visited .md-code-inline:before{background-color:#333}.lk-black .lk-link:active,a.lk-black:active{outline-color:#333}.lk-black .lk-link:hover,a.lk-black:hover{border-bottom-color:#333}.lk-black.lk-icon:hover{color:#333}.lk-gray .lk-link,a.lk-gray{color:#999}.lk-gray .lk-link:visited,a.lk-gray:visited{background-color:rgba(153,153,153,.04)}.lk-gray .lk-link:visited .md-code-inline:before,a.lk-gray:visited .md-code-inline:before{background-color:#999}.lk-gray .lk-link:active,a.lk-gray:active{outline-color:#999}.lk-gray .lk-link:hover,a.lk-gray:hover{border-bottom-color:#999}.lk-gray.lk-icon:hover{color:#999}.lk-white .lk-link,a.lk-white{color:#fcfcfc}.lk-white .lk-link:visited,a.lk-white:visited{background-color:rgba(252,252,252,.04)}.lk-white .lk-link:visited .md-code-inline:before,a.lk-white:visited .md-code-inline:before{background-color:#fcfcfc}.lk-white .lk-link:active,a.lk-white:active{outline-color:#fcfcfc}.lk-white .lk-link:hover,a.lk-white:hover{border-bottom-color:#fcfcfc}.lk-white.lk-icon:hover{color:#fcfcfc}.lk-orange .lk-link,a.lk-orange{color:#f3720d}.lk-orange .lk-link:visited,a.lk-orange:visited{background-color:rgba(243,114,13,.04)}.lk-orange .lk-link:visited .md-code-inline:before,a.lk-orange:visited .md-code-inline:before{background-color:#f3720d}.lk-orange .lk-link:active,a.lk-orange:active{outline-color:#f3720d}.lk-orange .lk-link:hover,a.lk-orange:hover{border-bottom-color:#f3720d}.lk-orange.lk-icon:hover{color:#f3720d}.lk-lilly .lk-link,a.lk-lilly{color:#7d5cff}.lk-lilly .lk-link:visited,a.lk-lilly:visited{background-color:rgba(125,92,255,.04)}.lk-lilly .lk-link:visited .md-code-inline:before,a.lk-lilly:visited .md-code-inline:before{background-color:#7d5cff}.lk-lilly .lk-link:active,a.lk-lilly:active{outline-color:#7d5cff}.lk-lilly .lk-link:hover,a.lk-lilly:hover{border-bottom-color:#7d5cff}.lk-lilly.lk-icon:hover{color:#7d5cff}.lk-blue .lk-link,a.lk-blue{color:#1a4d7f}.lk-blue .lk-link:visited,a.lk-blue:visited{background-color:rgba(26,77,127,.04)}.lk-blue .lk-link:visited .md-code-inline:before,a.lk-blue:visited .md-code-inline:before{background-color:#1a4d7f}.lk-blue .lk-link:active,a.lk-blue:active{outline-color:#1a4d7f}.lk-blue .lk-link:hover,a.lk-blue:hover{border-bottom-color:#1a4d7f}.lk-blue.lk-icon:hover{color:#1a4d7f}.lk-inherited .lk-link,a.lk-inherited{color:inherit}.lk-inherited .lk-link:active,a.lk-inherited:active{outline-color:inherit}.lk-inherited .lk-link:hover,a.lk-inherited:hover{border-bottom-color:inherit}.lk-inherited.lk-icon:hover{color:inherit}.lk-twitter .lk-link,a.lk-twitter{color:#55acee}.lk-twitter .lk-link:visited,a.lk-twitter:visited{background-color:rgba(85,172,238,.04)}.lk-twitter .lk-link:visited .md-code-inline:before,a.lk-twitter:visited .md-code-inline:before{background-color:#55acee}.lk-twitter .lk-link:active,a.lk-twitter:active{outline-color:#55acee}.lk-twitter .lk-link:hover,a.lk-twitter:hover{border-bottom-color:#55acee}.lk-twitter.lk-icon:hover{color:#55acee}.lk-facebook .lk-link,a.lk-facebook{color:#3b5998}.lk-facebook .lk-link:visited,a.lk-facebook:visited{background-color:rgba(59,89,152,.04)}.lk-facebook .lk-link:visited .md-code-inline:before,a.lk-facebook:visited .md-code-inline:before{background-color:#3b5998}.lk-facebook .lk-link:active,a.lk-facebook:active{outline-color:#3b5998}.lk-facebook .lk-link:hover,a.lk-facebook:hover{border-bottom-color:#3b5998}.lk-facebook.lk-icon:hover{color:#3b5998}.lk-clean{background-color:transparent}.lk-chrome:hover{color:#4989f4}.lk-teal .lk-link,a.lk-teal{color:#00a19c}.lk-teal .lk-link:visited,a.lk-teal:visited{background-color:rgba(0,161,156,.04)}.lk-teal .lk-link:visited .md-code-inline:before,a.lk-teal:visited .md-code-inline:before{background-color:#00a19c}.lk-teal .lk-link:active,a.lk-teal:active{outline-color:#00a19c}.lk-teal .lk-link:hover,a.lk-teal:hover{border-bottom-color:#00a19c}.lk-teal.lk-icon:hover{color:#00a19c}table{width:100%;border-spacing:0;border-collapse:separate}td,th{padding:8px}@media only screen and (max-width:950px){.table-responsive{display:block}.table-responsive>thead{display:none}.table-responsive>tbody,.table-responsive>tfoot{display:block}.table-responsive>tbody>tr,.table-responsive>tfoot>tr{display:block;background-color:rgba(203,204,188,.1);margin-bottom:30px}.table-responsive>tbody>tr:nth-child(even),.table-responsive>tfoot>tr:nth-child(even){background-color:rgba(203,204,188,.15)}.table-responsive>tbody>tr:last-child,.table-responsive>tfoot>tr:last-child{margin-bottom:0}.table-responsive>tbody>tr>td,.table-responsive>tfoot>tr>td{display:block;text-align:left}.table-responsive>tbody>tr>td:before,.table-responsive>tfoot>tr>td:before{display:block;content:attr(data-label);color:#555;font-size:13px;margin:0 0 5px}.table-responsive .table-responsive-hide{display:none}}.c-oreilly-teal{color:#00a19c}.c-yellow-highlight{color:#ffe270}.c-dark-orange{color:#f3720d}.c-pink{color:#e92c6c}.c-purple{color:#900070}.c-blue{color:#1a4d7f}.c-dark-turquoise{color:#1686a2}.c-dark-green{color:#1bc211}.c-white{color:#fcfcfc}.c-gray{color:#b7b7b7}.c-lilly{color:#7d5cff}.c-teal{color:#3aca96}.c-bg-oreilly-teal{background-color:#00a19c}.c-bg-yellow-highlight{background-color:#ffe270}.c-bg-dark-orange{background-color:#f3720d}.c-bg-pink{background-color:#e92c6c}.c-bg-purple{background-color:#900070}.c-bg-blue{background-color:#1a4d7f}.c-bg-dark-turquoise{background-color:#1686a2}.c-bg-dark-green{background-color:#1bc211}.c-bg-white{background-color:#fcfcfc}.c-bg-gray{background-color:#b7b7b7}.c-bg-lilly{background-color:#7d5cff}.c-bg-teal{background-color:#3aca96}.tj-emoji{width:1em;height:1em;margin:0 .05em 0 .1em;vertical-align:-.1em}.dc-header{margin-bottom:60px}.dc-heading{text-align:center;font-size:4em;padding:0 10px;margin-top:0;margin-bottom:10px}.dc-role{line-height:26px;padding:0 20px;margin-top:10px;text-align:center;font-style:italic;color:#cbccbc}.dc-colored{background:#f6f6f6;border-top:9px solid rgba(125,92,255,.5)}.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:800px}.md-markdown blockquote:not(.twitter-tweet){margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.md-markdown blockquote:not(.twitter-tweet) h1,.md-markdown blockquote:not(.twitter-tweet) h2,.md-markdown blockquote:not(.twitter-tweet) h3,.md-markdown blockquote:not(.twitter-tweet) h4,.md-markdown blockquote:not(.twitter-tweet) h5,.md-markdown blockquote:not(.twitter-tweet) h6{margin:-20px 0;padding:20px 0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block{margin:0}.md-markdown blockquote:not(.twitter-tweet) .md-code-block:last-child{margin-bottom:-20px}.md-markdown img:first-child{margin-top:0}.md-markdown img:last-child{margin-bottom:0}.md-markdown p:first-child{margin-top:0}.md-markdown p:last-child{margin-bottom:0}.md-markdown .md-code-block+ol,.md-markdown .md-code-block+ul,.md-markdown ol+.md-code-block,.md-markdown ul+.md-code-block{margin-top:35px}.md-markdown a:not(.md-heading){color:#e92c6c;border-bottom:1px solid transparent;background-color:rgba(255,255,255,.04)}.md-markdown a:not(.md-heading) .md-code-inline{position:relative}.md-markdown a:not(.md-heading) .md-code-inline:only-child{display:inline-block;line-height:17px}.md-markdown a:not(.md-heading) .md-code-inline:before{content:'';position:absolute;background-color:#f1e05a;top:0;left:0;right:0;bottom:0;opacity:.04}.md-markdown a:not(.md-heading):visited{background-color:rgba(233,44,108,.04)}.md-markdown a:not(.md-heading):visited .md-code-inline:before{background-color:#e92c6c}.md-markdown a:not(.md-heading):hover{border-bottom:1px solid #e92c6c}.md-markdown thead{background-color:#e0e0e0}.md-markdown tbody{background-color:#f6f6f6}.md-markdown tbody tr:nth-child(even){background-color:#f9f9f9}.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{padding:10px 0}.md-markdown h1:first-child,.md-markdown h2:first-child,.md-markdown h3:first-child,.md-markdown h4:first-child,.md-markdown h5:first-child,.md-markdown h6:first-child{margin-top:0}.md-markdown h1:last-child,.md-markdown h2:last-child,.md-markdown h3:last-child,.md-markdown h4:last-child,.md-markdown h5:last-child,.md-markdown h6:last-child{margin-bottom:0}.md-markdown iframe{display:block;margin:35px auto}.md-markdown iframe:first-child{margin-top:0}.md-markdown iframe:last-child{margin-bottom:0}.md-markdown li>p:only-child{display:inline}.md-markdown ul{list-style-type:none}.md-markdown ul>li:before{content:'◯';margin-right:5px}.md-heading{color:inherit}.md-markdown-inline{display:inline}.md-markdown-inline p{display:inline;line-height:initial}.md-code-block,.ocha-highlighted{margin:0 -20px}.md-code-block{padding:10px 40px;line-height:24px}.ocha-code-block{padding:18px 50px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{max-width:inherit!important;margin:0 -20px;padding-left:20px;padding-right:20px}.md-code-block .md-code{display:block}.md-markdown-summary p{margin:10px 0}@media only screen and (min-width:768px){.md-markdown h1,.md-markdown h2,.md-markdown h3,.md-markdown h4,.md-markdown h5,.md-markdown h6{margin-left:-40px;margin-right:-40px;padding:10px 40px}.md-code-block,.ocha-highlighted{margin:0 -40px}.md-markdown:not(.mm-content-html) .md-code-block+blockquote{margin:0 -40px;padding-left:40px;padding-right:40px}}@media only screen and (min-width:1300px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:900px}}@media only screen and (min-width:1400px){.md-markdown blockquote,.md-markdown code,.md-markdown ol,.md-markdown p,.md-markdown table,.md-markdown ul{max-width:1000px}.md-code-block,.md-markdown:not(.mm-content-html) .md-code-block+blockquote,.ocha-highlighted{margin:0 -400px 0 -40px}}.md-markdown-large blockquote,.md-markdown-large code,.md-markdown-large ol,.md-markdown-large p,.md-markdown-large table,.md-markdown-large ul{max-width:inherit}.md-footnotes{margin-top:80px;padding:20px 0;border-top:1px dashed #cbccbc}.md-footnote{margin:0;padding:8px 0;font-size:14px;display:-webkit-box;display:-moz-box;display:-webkit-flex;display:-ms-flexbox;display:box;display:flex;-webkit-box-align:center;-moz-box-align:center;-o-box-align:center;-ms-flex-align:center;-webkit-align-items:center;align-items:center}.md-footnote p{line-height:16px}.md-footnote-anchor{padding:2px 6px;margin-right:10px;text-align:center;font-weight:700;color:#e92c6c}.ly-custom-subheading .md-footnote-anchor,.ly-lean .md-footnote-anchor{font-family:Merriweather,Georgia,serif}.md-markdown a.md-footnote-anchor{border:1px solid #e92c6c}.md-markdown a.md-footnote-anchor:hover{background-color:#e92c6c;color:#fcfcfc}.md-footnote-ref{margin-left:1px;margin-right:3px}.md-markdown a.md-footnote-ref{border-bottom:1px dotted}.md-code-block,.md-code-inline{-webkit-text-size-adjust:none;-ms-text-size-adjust:none;text-size-adjust:none;background:#fcfcf5;color:#4d4d4c}.md-code{padding:8px 10px}.md-code-inline{padding:0 6px}a .md-code{color:inherit}.md-code-comment{color:#8e908c}.md-code-attribute,.md-code-built_in,.md-code-constant,.md-code-literal,.md-code-number,.md-code-params,.md-code-pragma,.md-code-preprocessor,.md-code-regexp,.md-code-tag,.md-code-variable,.md-lang-css .md-code-class,.md-lang-css .md-code-id,.md-lang-css .md-code-pseudo,.md-lang-html .md-code-doctype,.md-lang-ruby .md-code-constant,.md-lang-xml .md-code-doctype,.md-lang-xml .md-code-pi,.md-lang-xml .md-code-tag .md-code-title,color #c82829{color:#f5871f}.md-lang-css .md-code-rule .md-code-attribute,.md-lang-ruby .md-code-class .md-code-title{color:#eab700}.md-code-header,.md-code-inheritance,.md-code-name,.md-code-string,.md-code-value,.md-lang-ruby .md-code-symbol,.md-lang-xml .md-code-cdata{color:#718c00}.md-code-title,.md-lang-css .md-code-hexcolor{color:#3e999f}.md-code-function,.md-lang-coffeescript .md-code-title,.md-lang-javascript .md-code-title,.md-lang-perl .md-code-sub,.md-lang-python .md-code-decorator,.md-lang-python .md-code-title,.md-lang-ruby .md-code-function .md-code-title,.md-lang-ruby .md-code-title .md-code-keyword{color:#4271ae}.md-code-keyword,.md-lang-javascript .md-code-function{color:#8959a8}.md-lang-coffeescript .md-lang-javascript,.md-lang-javascript .md-lang-xml,.md-lang-tex .md-code-formula,.md-lang-xml .md-code-cdata,.md-lang-xml .md-lang-css,.md-lang-xml .md-lang-javascript,.md-lang-xml .vbscript{opacity:.5}.md-code-deletion{background-color:rgba(244,0,30,.05);color:#f4001e}.md-code-addition{background-color:rgba(27,194,17,.05);color:#1bc211}.md-mark{background-color:rgba(255,226,112,.5);color:inherit}.md-mark span{color:inherit}.md-heading-hover{cursor:pointer;background-color:rgba(22,134,162,.1)}.mde-text-left{text-align:left}.mde-text-center{text-align:center}.mde-text-right{text-align:right}.mde-size-small{font-size:12px}.mde-size-small,.mde-size-small p{line-height:12px}.mde-size-large{font-size:36px}.mde-size-large,.mde-size-large p{line-height:36px}.mde-size-giant{font-size:72px}.mde-size-giant,.mde-size-giant p{line-height:72px}.mde-clearfix:after{content:' ';display:block;visibility:hidden;clear:both;font-size:0;height:0}.mde-quote{margin:0;padding:20px 0;border:none;border-top:5px solid rgba(0,0,0,.05);border-bottom:5px solid rgba(0,0,0,.05)}.mde-core{font-family:'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}.mde-mono{font-family:Consolas,Menlo,Monaco,'Lucida Console','Liberation Mono','DejaVu Sans Mono','Bitstream Vera Sans Mono','Courier New',monospace,serif}.ly-custom-heading .mde-heading,.ly-lean .mde-heading{font-family:Neuton,'Hoefler Text',Palatino,Baskerville,Georgia,'Times New Roman',serif}.ly-custom-subheading .mde-subheading,.ly-lean .mde-subheading{font-family:Merriweather,Georgia,serif}.mde-pad-0{padding:0}.mde-pad-5{padding:5px}.mde-pad-10{padding:10px}.mde-pad-15{padding:15px}.mde-pad-20{padding:20px}.mde-pad-25{padding:25px}.mde-pad-30{padding:30px}.mde-pad-50{padding:50px}.mde-mar-0{margin:0}.mde-mar-5{margin:5px}.mde-mar-10{margin:10px}.mde-mar-15{margin:15px}.mde-mar-20{margin:20px}.mde-mar-25{margin:25px}.mde-mar-30{margin:30px}.mde-mar-50{margin:50px}@media only screen and (min-width:900px){.mde-left{float:left}.mde-right{float:right}.mde-inline{display:inline-block;vertical-align:top}.mde-20{max-width:20%}.mde-25{max-width:25%}.mde-33{max-width:33.3333333333%}.mde-50{max-width:50%}.mde-66{max-width:66.6666666666%}.mde-75{max-width:75%}}.twitter-tweet-figure{background:0 0;padding:0}.twitter-tweet-figure:before{content:none}.twitter-tweet-figure .twitter-tweet{margin:0 auto}blockquote.twitter-tweet{overflow:hidden;color:#1c2022;background-color:#fff;border:1px solid #e1e8ed;border-radius:4px;width:500px;max-width:100%;min-width:220px;padding:1.25rem 1.25rem .725rem}blockquote.twitter-tweet:before{content:none}blockquote.twitter-tweet p{white-space:pre-wrap;font:16px/1.4 Helvetica,Roboto,'Segoe UI',Calibri,'Helvetica Neue',HelveticaNeue,TeXGyreHeros,FreeSans,'Nimbus Sans L','Liberation Sans',Helvetica,Arial,sans-serif}blockquote.twitter-tweet a,blockquote.twitter-tweet a:visited{color:#2b7bb9}.at-header{padding:20px 0 80px;margin-bottom:0;min-height:250px;position:relative;background-repeat:no-repeat;background-position:center;-webkit-background-size:cover;-moz-background-size:cover;background-size:cover;background-color:#d9eaee}.at-header code{color:inherit;background-color:transparent}.at-title-wrapper{text-align:center;padding:0 20px;margin:0 10px}.at-title{display:inline;margin:0;padding:0;font-size:2.6em;line-height:1.1em;mix-blend-mode:hard-light;background-color:rgba(22,134,162,.8);color:#fcfcfc}.ly-custom-heading .at-title,.ly-lean .at-title{font-size:3em}.at-title .tj-emoji{mix-blend-mode:difference}.at-actions{height:35px;text-align:right}.at-teaser{margin-top:0;padding:0;text-align:left;color:inherit}.ly-custom-subheading .at-teaser,.ly-lean .at-teaser{font-family:Merriweather,Georgia,serif}.at-section{padding:20px}.at-section:empty{padding:0}.at-header-tags{margin:30px 20px}.at-header-tags .tg-tag-item{color:#fcfcfc}.at-header-tags .tg-tag{background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-siblings{margin:0 20px}.at-editor-note{background-color:rgba(250,238,241,.5)}.at-editor-note:after{display:block;text-align:right;font-style:italic;content:'— Editor’s note.';margin-top:10px}.ly-custom-subheading .at-editor-note:after,.ly-lean .at-editor-note:after{font-family:Merriweather,Georgia,serif}.at-body{position:relative}.at-body:before{position:absolute;left:0;right:0;bottom:-4px;height:4px;content:'';background:-webkit-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-moz-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-o-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:-ms-linear-gradient(left,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);background:linear-gradient(to right,#fcfcfc 0,#ffe270 66%,#e92c6c 100%);max-width:800px;margin:0 20px}.at-subscribe{margin:0 20px 40px}@media screen and (min-width:768px){.at-header{padding-bottom:50px}.at-header-tags{position:absolute;right:7px;top:7px;margin:0}.at-header-tags .tg-tag-item{display:block;text-align:right;margin-right:0;color:#fcfcfc}.at-header-tags .tg-tag{display:inline-block;background-color:rgba(85,85,85,.7)}@supports (mix-blend-mode:hard-light){.at-header-tags .tg-tag{mix-blend-mode:hard-light;background-color:rgba(144,0,112,.5)}}.at-title-wrapper{margin-left:0;margin-right:0;max-width:75%;text-align:left}.at-section{padding:20px 40px}.at-body:before{margin:0 40px}.at-subscribe{margin:0 40px 40px;max-width:800px}.at-siblings{max-width:800px;margin:0 40px}.at-title{font-size:3em}.ly-custom-heading .at-title{font-size:4em}}@media screen and (min-width:1200px){.at-container .de-sidebar{padding-top:20px}}@media screen and (min-width:1400px){.at-body:before,.at-siblings,.at-subscribe{max-width:1000px}}</style>]]></description><link>https://ponyfoo.com/articles/javascript-performance-pitfalls-v8</link><guid isPermaLink="true">https://ponyfoo.com/articles/javascript-performance-pitfalls-v8</guid><category><![CDATA[performance]]></category><category><![CDATA[v8]]></category><category><![CDATA[internals]]></category><dc:creator><![CDATA[benedikt.meurer@googlemail.com (Benedikt Meurer)]]></dc:creator><pubDate>Tue, 05 Mar 2019 16:32:03 GMT</pubDate></item></channel></rss>