<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Tony Wallace</title>
    <description>The latest articles on DEV Community by Tony Wallace (@irritant).</description>
    <link>https://dev.to/irritant</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F785828%2Fb7e3de0b-60ba-41fb-854a-097a5beb5371.png</url>
      <title>DEV Community: Tony Wallace</title>
      <link>https://dev.to/irritant</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/irritant"/>
    <language>en</language>
    <item>
      <title>Capture Groups in Regular Expressions</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Mon, 17 Apr 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/redbit/capture-groups-in-regular-expressions-4g7o</link>
      <guid>https://dev.to/redbit/capture-groups-in-regular-expressions-4g7o</guid>
      <description>&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Regular_Expressions" rel="noopener noreferrer"&gt;Regular expressions&lt;/a&gt; (regex) are powerful tools for pattern matching and string parsing. While the syntax can sometimes be difficult to understand, it can make many tasks more efficient. In this post, I'm going to demonstrate a feature of regular expressions called &lt;em&gt;capture groups&lt;/em&gt;.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;A few notes before we get started:&lt;/em&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;em&gt;JavaScript will be used for the examples in this post. The principles of regular expressions should be the same in other languages but some usage details may differ. If you aren't using JavaScript, refer to your language's documentation for specifics.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;&lt;em&gt;Any discussion of overall efficiency with regard to software has to take development time and maintenance into account along with the runtime performance of the code. If runtime performance is your ultimate priority, regular expressions may not always be the best choice, because their performance can vary greatly depending on the characteristics of the input, the regular expression pattern, and the regular expression engine. In terms of development time and maintenance, regular expressions can reduce the amount and complexity of code necessary to accomplish some tasks. This is often a balancing act, and you can test your solutions with a benchmarking app like &lt;a href="https://jsbench.me/" rel="noopener noreferrer"&gt;jsbench.me&lt;/a&gt; to be sure they align with your priorities. I also suggest writing unit tests for critical parsers to guard against regressions during future maintenance.&lt;/em&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What is a capture group?
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;capture group&lt;/em&gt; is a pattern within a regular expression that will be included in the result of calling &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec" rel="noopener noreferrer"&gt;RegExp.prototype.exec&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match" rel="noopener noreferrer"&gt;String.prototype.match&lt;/a&gt; or &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/matchAll" rel="noopener noreferrer"&gt;String.prototype.matchAll&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Lets start with some basic pattern matching. Say we want to parse a substring containing one or more digits from a pathname like &lt;code&gt;/items/42&lt;/code&gt;. The basic regular expression for that would be &lt;code&gt;/\/\d+/g&lt;/code&gt;. This pattern will search a string for all matches (the &lt;code&gt;g&lt;/code&gt; or global flag) of a forward slash (&lt;code&gt;\/&lt;/code&gt; — the preceding backslash is an escape) followed by one or more digits (&lt;code&gt;\d+&lt;/code&gt;). This works very well if you want to test that a string contains that pattern:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = /\/\d+/g;

re.test('/items/42'); // true
re.test('/items/42/1'); // true
re.test('/items/42/options/1'); // true
re.test('/items/new'); // false
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/test" rel="noopener noreferrer"&gt;RegExp.prototype.test&lt;/a&gt; returns true for any string that contains a forward slash followed by one or more digits, and false for any other string. Now let's try using &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/RegExp/exec" rel="noopener noreferrer"&gt;RegExp.prototype.exec&lt;/a&gt; to get some information about the match:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;re.exec('/items/42'); // [ '/42', index: 6, input: '/items/42' ]
re.exec('/items/42/1'); // [ '/1', index: 9, input: '/items/42/1' ]
re.exec('/items/42/options/1'); // [ '/1', index: 17, input: '/items/42/options/1' ]
re.exec('/items/new'); // null
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;RegExp.prototype.exec&lt;/code&gt; returns an array unless the string doesn't match the regex, in which case it returns &lt;code&gt;null&lt;/code&gt;. The array for each match contains one item, which is the string that matched. Additional properties attached to the array describe the &lt;code&gt;index&lt;/code&gt; at which the string appears and the original &lt;code&gt;input&lt;/code&gt; string. (You can access these properties via subscripting or dot notation, just like the properties of any object.) In cases where the input string contains more than one occurrence of the pattern &lt;code&gt;/\/\d+/&lt;/code&gt; (e.g. &lt;code&gt;'/items/42/options/1'&lt;/code&gt;), note that only the last occurrence has been matched. This is due to the use of the &lt;code&gt;g&lt;/code&gt; flag on the regex. You may omit the &lt;code&gt;g&lt;/code&gt; flag if you only care about the first match, but we'll benefit from it in a moment.&lt;/p&gt;

&lt;p&gt;Now let's try &lt;code&gt;String.prototype.match&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;'/items/42'.match(re); // [ '/42' ]
'/items/42/1'.match(re); // [ '/42', '/1' ]
'/items/42/options/1'.match(re); // [ '/42', '/1' ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;These results include every occurrence of the pattern in each string. If we had not used the &lt;code&gt;g&lt;/code&gt; flag on the pattern, we would have gotten the same result as calling &lt;code&gt;RegExp.prototype.exec&lt;/code&gt; without the &lt;code&gt;g&lt;/code&gt; flag — only the first match would be included, and the array would have additional properties for &lt;code&gt;index&lt;/code&gt; and &lt;code&gt;input&lt;/code&gt;. You can obtain the best of both approaches by using &lt;code&gt;String.prototype.matchAll&lt;/code&gt;, which will return the input and index for every match:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Array.from('/items/42/options/1'.matchAll(re));

// [
//   [ '/42', index: 6, input: '/items/42/options/1' ],
//   [ '/1', index: 9, input: '/items/42/options/1' ]
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There are tho things that are important to note:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We're using &lt;code&gt;Array.from&lt;/code&gt; to coerce the result into an array. The return value of &lt;code&gt;String.prototype.matchAll&lt;/code&gt; is an &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators" rel="noopener noreferrer"&gt;iterator&lt;/a&gt; but in many cases it's  easier to work with an array.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;String.prototype.matchAll&lt;/code&gt; requires a global regex, meaning that you must include the &lt;code&gt;g&lt;/code&gt; flag.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We could actually work with this result if we wanted to hack around a bit. The following code will extract the digits from each match by replacing the forward slash with an empty string:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const matches = Array.from('/items/42/options/1'.matchAll(re));

// [
//   [ '/42', index: 6, input: '/items/42/options/1' ],
//   [ '/1', index: 9, input: '/items/42/options/1' ]
// ]

const params = matches.map((match) =&amp;gt; match[0].replace('/', '')); // [ '42', '1' ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This accomplishes our basic task of extracting the digits, but isn't particularly clean. Fortunately, there's a better way. We can use a &lt;em&gt;capture group&lt;/em&gt; to isolate the digits. The capture group is indicated by parentheses surrounding the part of the pattern you with to capture. In this case, one or more digits as specified by &lt;code&gt;\d+&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = `/\/(\d+)/g`;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;If we use &lt;code&gt;String.prototype.matchAll&lt;/code&gt; with this regex, we get a second array element for each match, which contains the digits we captured without the forward slash (because the forward slash was not included in the capture group). We can extract the digits simply by accessing the second element in the array for each match:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const matches = Array.from('/items/42/options/1'.matchAll(re));

// [
//   [ '/42', '42', index: 6, input: '/items/42/options/1' ],
//   [ '/1', '1', index: 9, input: '/items/42/options/1' ]
// ]

const params = matches.map((match) =&amp;gt; match[1]); // [ '42', '1' ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Capture groups give you an efficient way to extract substrings that match certain patterns from a larger string. There are many cases in which this will be sufficient, but sometimes the results can be awkward to work with. Maybe you need to relate the values extracted in the previous example to property names, where the first value should be called &lt;code&gt;itemId&lt;/code&gt; and the second &lt;code&gt;optionId&lt;/code&gt;. This would require more code to create an object containing these properties. One way to do this is to reduce the matches into an object, in which each value is keyed by the property name:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const matches = Array.from('/items/42/options/1'.matchAll(re));
const propertyNames = ['itemId', 'optionId'];

const properties = matches.reduce((obj, match, idx) =&amp;gt; {
  const name = propertyNames[idx];
  const value = match[1];
  return { ...obj, [name]: value };
}, {});

// { itemId: '42', optionId: '1' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again, this works but isn't very graceful. Relying on code to relate the matches to the corresponding property names could be fragile in some cases, depending on the specific regex and string that are being parsed. This is where &lt;em&gt;named capture groups&lt;/em&gt; may help.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is a named capture group?
&lt;/h2&gt;

&lt;p&gt;A &lt;em&gt;named capture group&lt;/em&gt; is similar to a regular capture group, but the syntax allows us to embed a name for each capture group in the regex. The syntax for a named capture group with the name &lt;code&gt;id&lt;/code&gt; and pattern &lt;code&gt;\d+&lt;/code&gt; will be &lt;code&gt;(?&amp;lt;id&amp;gt;\d+)&lt;/code&gt;. The name is placed within angle brackets, preceded by a question mark, and followed by the pattern. If we apply this to our previous example, we can obtain the named values in the &lt;code&gt;groups&lt;/code&gt; property on each match:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = `/\/(?&amp;lt;id&amp;gt;\d+)/g`;

const matches = Array.from('/items/42/options/1'.matchAll(re));

// [
//   [ '/42', '42', index: 6, input: '/items/42/options/1', groups: { id: '42' } ],
//   [ '/1', '1', index: 9, input: '/items/42/options/1', groups: { id: '1' } ]
// ]

const params = matches.map((match) =&amp;gt; match.groups.id); // [ '42', '1' ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now, you might have noticed that this doesn't solve our problem of needing extra code to resolve the values to property names, because the values are still distributed across separate matches and all have the same name. There's still no direct route to convert them to the object we want, which is &lt;code&gt;{ itemId: '42', optionId: '1' }&lt;/code&gt;. We can change that by redesigning our regular expression. The following example uses a much more specific regex that describes the complete path we expect to match, including named capture groups for &lt;code&gt;itemId&lt;/code&gt; and &lt;code&gt;optionId&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = /^\/items\/(?&amp;lt;itemId&amp;gt;\d+)\/options\/(?&amp;lt;optionId&amp;gt;\d+)$/g;

const matches = Array.from('/items/42/options/1'.matchAll(re));

// [
//   [
//     '/items/42/options/1',
//     '42',
//     '1',
//     index: 0,
//     input: '/items/42/options/1',
//     groups: { itemId: '42', optionId: '1' }
//   ]
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can obtain the named values simply by accessing the &lt;code&gt;groups&lt;/code&gt; property of the first match:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [ match ] = Array.from('/items/42/options/1'.matchAll(re));

if (match) {
  const { groups } = match;
  // Work with the groups
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;(It's safe to assume that we can take the first match, if there is one, because the regex is anchored to the beginning and end of the string with &lt;code&gt;^&lt;/code&gt; and &lt;code&gt;$&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;The benefit of using named capture groups in this scenario is that names can be assigned to the values at the same time as they are parsed.&lt;/p&gt;

&lt;h2&gt;
  
  
  Choose your own adventure
&lt;/h2&gt;

&lt;p&gt;In the previous example, we gained the ability to parse named values from a string in one shot, with no code other than what was necessary to execute the regex and access the result. We also lost the ability to use the generic regular expression we started with, because of the need to assign a unique name to each value. In reality neither approach is perfect for all situations. Unnamed capture groups enable you to use generic regex patterns because you don't have to worry about providing a unique name for each pattern, but traversing the results can be messy and error prone. Named capture groups can make parsing much cleaner, but only if the strings you have to parse have a consistent structure. Regular expressions that contain named capture groups may also be harder to understand. So which should you choose? Asking a series of questions may help:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Would named capture groups provide a real benefit?

&lt;ul&gt;
&lt;li&gt;If yes, go to 2.&lt;/li&gt;
&lt;li&gt;If no, go to 3.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Do the strings you need to parse have a consistent enough structure to make named capture groups feasible?

&lt;ul&gt;
&lt;li&gt;If yes, go to 4.&lt;/li&gt;
&lt;li&gt;If no, go to 5.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Regular expressions that use unnamed capture groups are usually simpler and easier to understand, so it might be best to use them even if named capture groups might work in your scenario. Evaluate both options.&lt;/li&gt;
&lt;li&gt;Sounds like you might have found a good use case for named capture groups! Go to 6.&lt;/li&gt;
&lt;li&gt;Is there a reasonable workaround for the consistency problem? For example, could you design a regex that would cover all your input cases? (Without creating a nightmare for yourself and others?)

&lt;ul&gt;
&lt;li&gt;If yes, go to 6.&lt;/li&gt;
&lt;li&gt;If no, go to 7.&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Time to experiment! Test your workaround in comparison regular capture groups and any other approaches that you have in mind, and choose whichever one works best.&lt;/li&gt;
&lt;li&gt;Named capture groups are probably out of the question, so see if unnamed capture groups can do the job, or if you need to find a different solution.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Combining and nesting capture groups
&lt;/h2&gt;

&lt;p&gt;Capture groups may be combined and nested. Consider the following regular expression:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = /^\/items\/(?&amp;lt;itemId&amp;gt;\d+)(\/options\/(?&amp;lt;optionId&amp;gt;\d+))?$/g
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is similar to our previous regex, but a capture group has been added around the &lt;code&gt;/options&lt;/code&gt; subpath: &lt;code&gt;(\/options\/(?&amp;lt;optionId&amp;gt;\d+))?&lt;/code&gt;. The question mark after the closing parenthesis makes the capture group optional, which means that this regex will match the path with or without the &lt;code&gt;/options&lt;/code&gt; subpath. (E.g. &lt;code&gt;/items/42/options/1&lt;/code&gt; or &lt;code&gt;/items/42&lt;/code&gt;.)&lt;/p&gt;

&lt;p&gt;If we use this regex to match the full path, we get the following result:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [ match ] = Array.from('/items/42/options/1'.matchAll(re));

// [
//   '/items/42/options/1',
//   '42',
//   '/options/1',
//   '1',
//   index: 0,
//   input: '/items/42/options/1',
//   groups: { itemId: '42', optionId: '1' }
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The array contains four elements:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The match for the full pattern (&lt;code&gt;'/items/42/options/1'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The match for the &lt;code&gt;itemId&lt;/code&gt; named capture group (&lt;code&gt;'42'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The match for the unnamed capture group around the &lt;code&gt;/options&lt;/code&gt; subpath (&lt;code&gt;'/options/1'&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;The match for the &lt;code&gt;optionId&lt;/code&gt; named capture group (&lt;code&gt;'1'&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;code&gt;groups&lt;/code&gt; property contains the &lt;code&gt;itemId&lt;/code&gt; and &lt;code&gt;optionId&lt;/code&gt; values that were parsed from the path.&lt;/p&gt;

&lt;p&gt;Now let's match the path without the &lt;code&gt;/options&lt;/code&gt; subpath:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const [ match ] = Array.from('/items/42'.matchAll(re));

// [
//   '/items/42',
//   '42',
//   undefined,
//   undefined,
//   index: 0,
//   input: '/items/42',
//   groups: { itemId: '42', optionId: undefined }
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Once again, the array contains four elements for the full pattern match, &lt;code&gt;itemId&lt;/code&gt;, &lt;code&gt;/options&lt;/code&gt; subpath and &lt;code&gt;optionId&lt;/code&gt;, but the &lt;code&gt;/options&lt;/code&gt; subpath and &lt;code&gt;optionId&lt;/code&gt; are both &lt;code&gt;undefined&lt;/code&gt; because they're optional and were not present in the input. The &lt;code&gt;groups&lt;/code&gt; property contains the &lt;code&gt;itemId&lt;/code&gt; and &lt;code&gt;optionId&lt;/code&gt; values, but &lt;code&gt;optionId&lt;/code&gt; is similarly &lt;code&gt;undefined&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;We could also use a named capture group around the &lt;code&gt;/options&lt;/code&gt; subpath to include it in the groups:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const re = /^\/items\/(?&amp;lt;itemId&amp;gt;\d+)(?&amp;lt;subpath&amp;gt;\/options\/(?&amp;lt;optionId&amp;gt;\d+))?$/g

const [ match ] = Array.from('/items/42/options/1'.matchAll(re));

// [
//   '/items/42/options/1',
//   '42',
//   '/options/1',
//   '1',
//   index: 0,
//   input: '/items/42/options/1',
//   groups: { itemId: '42', subpath: '/options/1', optionId: '1' }
// ]

const [ match ] = Array.from('/items/42'.matchAll(re));

// [
//   '/items/42',
//   '42',
//   undefined,
//   undefined,
//   index: 0,
//   input: '/items/42',
//   groups: { itemId: '42', subpath: undefined, optionId: undefined }
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Again, the &lt;code&gt;subpath&lt;/code&gt; group is &lt;code&gt;undefined&lt;/code&gt; in the second example because it wasn't present in the input.&lt;/p&gt;

&lt;p&gt;The nested capture groups in these examples have enabled the regular expression to match variations on a path and parse the &lt;code&gt;itemId&lt;/code&gt; and &lt;code&gt;optionId&lt;/code&gt; whether the &lt;code&gt;/options&lt;/code&gt; subpath is present or not, all in just a few lines of code. Try applying these techniques to simple string parsing tasks so you can get a feel for how they work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary
&lt;/h2&gt;

&lt;p&gt;In this post, we've learned how capture groups can make many string parsing tasks easier, but remember that regular expressions aren't the right solution. Some use cases that could be awkward or slow with regular expressions may be satisfied perfectly well by manipulating strings with code. Others may call for a more robust solution like &lt;a href="https://pegjs.org/" rel="noopener noreferrer"&gt;pegjs&lt;/a&gt;. As always, experiment with all the tools you have at your disposal and see what works best for your application.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>regex</category>
      <category>webdev</category>
    </item>
    <item>
      <title>Automated Testing with Playwright</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Tue, 28 Mar 2023 13:00:00 +0000</pubDate>
      <link>https://dev.to/redbit/automated-testing-with-playwright-1ni7</link>
      <guid>https://dev.to/redbit/automated-testing-with-playwright-1ni7</guid>
      <description>&lt;p&gt;There are four basic types of software test that can be automated.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;End-to-end testing of user flows with live data (integration testing)&lt;/li&gt;
&lt;li&gt;End-to-end testing of user flows with mock data (isolated user interface testing)&lt;/li&gt;
&lt;li&gt;Isolated testing of individual components&lt;/li&gt;
&lt;li&gt;Unit testing&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This article will focus on end-to-end testing, but first a few notes about the other types of tests.&lt;/p&gt;

&lt;p&gt;Playwright has experimental support for testing components in React, Vue and Svelte. I have not yet been able to integrate this into one of RedBit's React projects because Playwright uses a different bundler than us. (Playwright uses Vite while we use Webpack.) Many of our components rely on specific Webpack configuration and some custom plugins that can't easily be replicated in Vite. While I'm sure this is a surmountable problem, I'm not sure it's worth the effort. It would likely take less time to build out pages to render components that would be testable with the regular Playwright APIs, and could also serve as a reference library for developers.&lt;/p&gt;

&lt;p&gt;Playwright isn't first and foremost a unit test runner, so I won't discuss it in that context. RedBit uses Jest or unit testing in web projects. Jest uses a similar assertion syntax to Playwright, which helps reduce cognitive overhead for developers.&lt;/p&gt;

&lt;h2&gt;
  
  
  End-to-end user flow testing
&lt;/h2&gt;

&lt;p&gt;End-to-end testing of user flows aims to simulate the actions a user would perform while using an app, and verify that those actions have the expected outcomes. This is Playwright's main purpose.&lt;/p&gt;

&lt;h3&gt;
  
  
  Automating a user flow
&lt;/h3&gt;

&lt;p&gt;Writing automated end-to-end tests normally involves determining the sequence of actions that a user would perform in the app, translating them manually to code, then adding assertions to verify that the expected actions were actually performed. For example, you might navigate to a certain page in your app and verify that the browser's location is set to the expected url, then simulate a click on a link and verify that the browser's location has changed to the link's url. At points along the way you might want to verify that certain messages or other components are visible on screen.&lt;/p&gt;

&lt;p&gt;Playwright provides a &lt;a href="https://playwright.dev/docs/codegen-intro" rel="noopener noreferrer"&gt;test generator&lt;/a&gt; that takes a lot of the drudgery out of automating tests for long user flows. It launches your app in a Chromium instance alongside a second process that records all the actions you perform in the app. You navigate through a user flow in your app and the test generator translates your actions to code. The test generator will also add some basic assertions, like testing that the browser location updates to the expected url when you click a link. You can then copy the test code to your project and add other assertions manually.&lt;/p&gt;

&lt;p&gt;The test generator worked quite well for the flows that I automated, except that it failed to capture the browser back button. That resulted in tests that would fail unless modified to restore the missing navigation actions. Even if the code output by the test generator needs some work, it's still a win in my opinion. The effort necessary to fix the tests will likely be far less than the effort that would have been required to write them from scratch.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing with mock data
&lt;/h3&gt;

&lt;p&gt;When you run the test generator, it launches your app, which is presumably backed by an API or some other data source. The data source might be a production environment (but hopefully not), or a remote test environemnt, or maybe a dev environment on your local computer. Either way, you're testing with live data. The problem with live data is that it's often subject to change, and when it changes your tests will probably fail. Consider the following scenario:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to a page &lt;code&gt;/products&lt;/code&gt; that renders a list of products.&lt;/li&gt;
&lt;li&gt;Click the first item to navigate to &lt;code&gt;/products/&amp;lt;id&amp;gt;&lt;/code&gt;, which displays details about an individual product. In this example, &lt;code&gt;&amp;lt;id&amp;gt;&lt;/code&gt; represents an &lt;code&gt;id&lt;/code&gt; property that is assigned to the product's database record.&lt;/li&gt;
&lt;li&gt;Assert that the details page url contains the correct &lt;code&gt;id&lt;/code&gt; property.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The Playwright test generator will write code that performs these actions based on rendered data, which will look more like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;/products&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the link that contains the text "Cuisinart Food Processor".&lt;/li&gt;
&lt;li&gt;Assert that the details page url is &lt;code&gt;/products/34&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    test('Products list and detail navigation flow', async ({ page }) =&amp;gt; {
      // Navigate to the products page:
      await page.goto('/products');
      await expect(page).toHaveURL('/products');

      // Click the "Cuisinart Food Processor" link:
      await page.getByRole('link', { name: 'Cuisinart Food Processor' }).click();
      await expect(page).toHaveURL('/products/34');

      // Navigate back to the products page:
      await page.goBack();
      await expect(page).toHaveURL('/products');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This will only work as long as the first product in the list is "Cuisinart Food Processor" with an id of 34. If the list is updated and another product is now first in the list, or if you test in another environment where the products have different ids, the test will fail. There are two solutions to this.&lt;/p&gt;

&lt;h4&gt;
  
  
  Mocking API responses
&lt;/h4&gt;

&lt;p&gt;The easiest solution is to fulfill API requests with mock data. Playwright provides a way to do this simply and cleanly by intercepting requests to a particular route:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    test('Products list and detail navigation flow', async ({ page }) =&amp;gt; {
      // Fulfill the products list API request with mock data.
      await page.route('/api/products', (route) =&amp;gt; {
        return route.fulfill({
          status: 200,
          body: JSON.stringify([
            { id: 34, name: 'Cuisinart Food Processor' },
            { id: 75, name: 'Vitamix Blender' },
          ]),
        });
      });

      // Navigate to the products page:
      await page.goto('/products');
      await expect(page).toHaveURL('/products');

      // Click the "Cuisinart Food Processor" link:
      await page.getByRole('link', { name: 'Cuisinart Food Processor' }).click();
      await expect(page).toHaveURL('/products/34');

      // Navigate back to the products page:
      await page.goBack();
      await expect(page).toHaveURL('/products');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In this example, a request to &lt;code&gt;/api/products&lt;/code&gt; will be fullfilled with the JSON-serialized test data. (It is assumed that we're mocking an API that sends JSON responses, but you can replace the test data with whatever is appropriate for your application.)&lt;/p&gt;

&lt;p&gt;With reliable test data, you can be assured that the first item in the list of products will never change, unless you change it. As long as the app's behaviour remains the same, your tests will always pass. This is not to say that you should &lt;em&gt;never&lt;/em&gt; test with live data. If you're running an integration test, you may need to verify a complex series of actions during which data must be written to, read from, and deleted from a database. However, there will be many situations in which you will only be concerned with testing one part of the system (e.g. the user interface) and you should be able to run your tests in isolation.&lt;/p&gt;

&lt;h4&gt;
  
  
  Testing based on structure, not content
&lt;/h4&gt;

&lt;p&gt;Returning to the test case we looked at before, we can see that it relies on specific content to locate the first product on the page:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;/products&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Click the link that contains the text "Cuisinart Food Processor".&lt;/li&gt;
&lt;li&gt;Assert that the details page url is &lt;code&gt;/products/34&lt;/code&gt;.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    test('Products list and detail navigation flow', async ({ page }) =&amp;gt; {
      // Navigate to the products page:
      await page.goto('/products');
      await expect(page).toHaveURL('/products');

      // Click the "Cuisinart Food Processor" link:
      await page.getByRole('link', { name: 'Cuisinart Food Processor' }).click();
      await expect(page).toHaveURL('/products/34');

      // Navigate back to the list:
      await page.goBack();
      await expect(page).toHaveURL('/products');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice that this test doesn't care where the "Cuisinart Food Processor" link is rendered. We're expecting it to be in a list of products but the test doesn't verify that. It could be anywhere on the page. That may or may not be important to you, but it's worth pointing out.&lt;/p&gt;

&lt;p&gt;We could rewrite this sequence to depend on page structure, instead:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Navigate to &lt;code&gt;/products&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Extract the detail page url from the first link in the products list.&lt;/li&gt;
&lt;li&gt;Click that same link.&lt;/li&gt;
&lt;li&gt;Assert that the details page url matches the url from step 2.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A test written this way would be content-agnostic and target elements precisely:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    test('Products list and detail navigation flow', async ({ page }) =&amp;gt; {
      // Navigate to the products page:
      await page.goto('/products');
      await expect(page).toHaveURL('/products');

      // Get the first link in the products list and extract the detail page url:
      const link = page.locator('.ul.products &amp;gt; li &amp;gt; a').nth(0);
      const url = await link.evaluate((node) =&amp;gt; node.getAttribute('href'));

      // Navigate to the product detail page:
      await link.click();
      await expect(page).toHaveURL(url);

      // Navigate back to the products list:
      await page.goBack();
      await expect(page).toHaveURL('/products');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The trade off is that you need technical knowledge of your app to write tests based on structure. This approach may not be feasible depending on who in your organization will be responsible for testing. It isn't a replacement for reliable test data, but provides another way to make your tests more accurate and resilient.&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing API requests
&lt;/h3&gt;

&lt;p&gt;You may have cases in which it is important to verify that your app makes specific API requests. For example, you might want to test that a new API request is made when the user selects a filter, and that the request is configured with the filter they selected. Playwright allows you to wait for a request and obtain information about it. The following example verifies that a &lt;code&gt;GET&lt;/code&gt; request is made for the &lt;code&gt;/products&lt;/code&gt; list with certain pagination and sort params:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    test('A request is made for the first page of products in descending order of creation', async ({ page }) =&amp;gt; {
      const request = await page.waitForRequest('/products**');
      await page.waitForLoadState('networkidle');

      // Verify that the request was configured correctly:
      const url = new URL(request.url);

      // Expect a GET request to /products?offset=0&amp;amp;limit=10&amp;amp;orderBy=createdAt&amp;amp;order=desc
      await expect(request.method).toEqual('GET');
      await expect(url.searchParams.get('offset')).toEqual('0');
      await expect(url.searchParams.get('limit')).toEqual('10');
      await expect(url.searchParams.get('orderBy')).toEqual('createdAt');
      await expect(url.searchParams.get('order')).toEqual('desc');
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If the request is made with any other method, or any other values for the &lt;code&gt;offset&lt;/code&gt;, &lt;code&gt;limit&lt;/code&gt;, &lt;code&gt;orderBy&lt;/code&gt; and &lt;code&gt;order&lt;/code&gt; params, the request will fail.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: The wildcard (&lt;code&gt;**&lt;/code&gt;) at the end of the url tells Playwright to match any request for &lt;code&gt;/products&lt;/code&gt; regardless of the query params. Without it, the request would only be matched if it was made without any query params.&lt;/em&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Testing rendering accuracy
&lt;/h3&gt;

&lt;p&gt;If you have a reliable and stable source of test data (see &lt;em&gt;Mocking API responses&lt;/em&gt;) it's possible to test that your data was rendered according to requirements. The process is as follows.&lt;/p&gt;

&lt;p&gt;For each item in your test data:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prepare the properties of the test data as you would expect them to have been rendered. For example, if you have a number formatter that renders a number (&lt;code&gt;25.00&lt;/code&gt;) as a currency string (&lt;code&gt;'$25.00'&lt;/code&gt;), apply it to the number. (If your user interface is localized, make sure your tests use the same locale as the app. If you're being thorough, you may want to run separate tests for each locale.)&lt;/li&gt;
&lt;li&gt;Locate the element that corresponds to each property in the DOM tree and extract the rendered value.&lt;/li&gt;
&lt;li&gt;Assert that the rendered values are equal to the formatted values.
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    import { productsTestData } from './test-data';
    import { formatCurrency } from './utilities/currency';

    test('Products list renders as expected', async ({ page }) =&amp;gt; {
      await page.goto('/products');
      await expect(page).toHaveURL('/products');

      for (let i = 0; i &amp;lt; productsTestData.length; i++) {
        const product = productsTestData[i];

        // Format the expected values.
        const expectedLink = `/products/${product.id}`;
        const expectedName = product.name;
        const expectedPrice = formatCurrency(product.price);

        // Get the DOM node that contains the product.
        const node = await page.locator('.ul.products &amp;gt; li').nth(i).evaluate((node) =&amp;gt; node);

        // Get the rendered link href.
        const renderedLink = node.querySelector('a').getAttribute('href');

        // Get the rendered product name and price.
        // Trim the values to ignore any whitespace introducted during rendering.
        const renderedName = node.querySelector('.product-name').textContent.trim();
        const renderedPrice = node.querySelector('.product-price').textContent.trim();

        // Assert that the rendered values equal the expected values.
        expect(renderedLink).toEqual(expectedLink);
        expect(renderedName).toEqual(expectedName);
        expect(renderedPrice).toEqual(expectedPrice);
      }
    });
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  How much should you test?
&lt;/h2&gt;

&lt;p&gt;The complexity of your end-to-end tests will more or less reflect your application's complexity. The more information you render, the more you have to test. The example above only expects the link, product name and product price to be rendered as specific strings. It doesn't test that the layout and styling are correct, or even that the elements are visible. It's possible to write more comprehensive tests, but doing so requires more development time. Your tests will likely be invalidated more often, which will result in more failures. There are costs to consider and questions to ask:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;How mature is your product? Is your user interface subject to frequent design changes or is it stable?&lt;/li&gt;
&lt;li&gt;Can you afford the impact that more complex tests and more frequent failures will have on your dev team's velocity?&lt;/li&gt;
&lt;li&gt;At what point does the cumulative cost of test development and maintenance exceed the cost of human QA testing?&lt;/li&gt;
&lt;li&gt;At what point does the added time pressure cause developers to give up and remove failing tests instead of fixing them, rendering your investment pointless?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are no right answers to these questions. The testing strategy you choose should depend on your organization's priorities and may evolve over time. An early-stage startup might prioritize high-level testing of user flows and limit rendering tests to the critical path. As the organization matures and their capacity improves, they might start to add tests for other parts of their product, or make existing tests more comprehensive, or both.&lt;/p&gt;

&lt;p&gt;Remember that the goals of automated testing are to reduce human time and effort, and to improve consistency. The highest value automated tests are those that have to be run most often and require the most attention to detail – complex flows on your application's critical path. Start by identifying opportunities to reduce labour in those areas and gradually increase yor test coverage from there.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>testing</category>
      <category>playwright</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Small Viewport Units in CSS</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Mon, 09 Jan 2023 14:00:00 +0000</pubDate>
      <link>https://dev.to/redbit/small-viewport-units-in-css-11d2</link>
      <guid>https://dev.to/redbit/small-viewport-units-in-css-11d2</guid>
      <description>&lt;p&gt;In this post, I'll give a short demonstration of how small viewport units in CSS can be used to build a grid that keeps a certain number of elements above the fold on all devices.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction to Viewport Units
&lt;/h2&gt;

&lt;p&gt;Let's begin with a quick primer on CSS viewport units. You may already be familiar with the &lt;code&gt;vh&lt;/code&gt; and &lt;code&gt;vw&lt;/code&gt; units, which have been around for a while and have broad browser support. These units specify dimensions as a percentage of the viewport height (&lt;code&gt;vh&lt;/code&gt;) or width (&lt;code&gt;vw&lt;/code&gt;). These are distinct from percentage (&lt;code&gt;%&lt;/code&gt;) units, which specify an element's dimensions as a percentage of one of its ancestor's dimensions. For example, &lt;code&gt;100vh&lt;/code&gt; is 100% of the viewport height while &lt;code&gt;5vw&lt;/code&gt; is 5% of the viewport width. There are additional units like &lt;code&gt;vmin&lt;/code&gt; and &lt;code&gt;vmax&lt;/code&gt;, which express dimensions as a percentage of the minimum and maximum viewport size, respectively. For example, &lt;code&gt;50vmax&lt;/code&gt; will be equal to &lt;code&gt;50vh&lt;/code&gt; in portrait orientation or &lt;code&gt;50vw&lt;/code&gt; in landscape orientation.&lt;/p&gt;

&lt;p&gt;The trouble with &lt;code&gt;wh&lt;/code&gt; and &lt;code&gt;vw&lt;/code&gt; is that the full dimensions of the viewport aren't always available to your application, especially on mobile devices. Browser controls like the location bar will occupy some of that space at least some of the time, which means that an element whose height is &lt;code&gt;100vh&lt;/code&gt; will overflow the available space and some of its content will be hidden when the browser controls are visible. There is a good explanation of this problem at &lt;a href="https://web.dev/viewport-units/" rel="noopener noreferrer"&gt;web.dev&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;If you're trying to keep certain content above the fold, you can’t reliably use &lt;code&gt;100vh&lt;/code&gt; as an indicator of how much space will be available before the user has to scroll. You also can’t reliably predict the amount of space that will be occupied by browser controls because it varies by operating system and browser. (Otherwise you could probably get away with a rule like &lt;code&gt;height: calc(100vh - &amp;lt;location bar height in px&amp;gt;)&lt;/code&gt;, but please don't do that. It's an invitation to have your site broken by a future OS or browser update.)&lt;/p&gt;

&lt;p&gt;This problem can be solved by taking advantage of three new types of units:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The small viewport (&lt;code&gt;svh&lt;/code&gt;/&lt;code&gt;svw&lt;/code&gt;/&lt;code&gt;svmin&lt;/code&gt;/&lt;code&gt;svmax&lt;/code&gt;): These units give you the height, width, minimum dimension and maximum dimension of the viewport when the browser chrome is visible (e.g. on page load or after scrolling to the top of the page).&lt;/li&gt;
&lt;li&gt;The large viewport (&lt;code&gt;lvh&lt;/code&gt;/&lt;code&gt;lvw&lt;/code&gt;/&lt;code&gt;lvmin&lt;/code&gt;/&lt;code&gt;lvmax&lt;/code&gt;): These units give you the height, width, minimum dimension and maximum dimension of the viewport when the browser chrome is hidden (e.g. after scrolling down the page).&lt;/li&gt;
&lt;li&gt;The dynamic viewport (&lt;code&gt;dvh&lt;/code&gt;/&lt;code&gt;dvw&lt;/code&gt;/&lt;code&gt;dvmin&lt;/code&gt;/&lt;code&gt;dvmax&lt;/code&gt;): These units give you the small or large viewport dynamically, depending on whether the browser chrome is currently visible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These units are relatively new to CSS but have, at the time of writing, been implemented in Chrome, Edge, Firefox and Safari. You can check the current support for this feature on &lt;a href="https://caniuse.com/mdn-css_types_length_viewport_percentage_units_dynamic" rel="noopener noreferrer"&gt;caniuse&lt;/a&gt;. (This link is for dynamic viewport units, but browser support should be the same for the small and large viewport units.) If you need to support older browsers that haven't implemented these units, consider using fallback rules. This strategy will allow you to provide the best experience for newer browsers at the cost of delivering a less optimal (but still good) experience on older browsers.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Grid Layout
&lt;/h2&gt;

&lt;h3&gt;
  
  
  HTML
&lt;/h3&gt;

&lt;p&gt;The HTML for this demo is about as simple as it gets — an unordered list that contains twelve items, each of which contains a number.&lt;/p&gt;

&lt;h5&gt;
  
  
  index.html
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;&amp;lt;!DOCTYPE html&amp;gt;
&amp;lt;html&amp;gt;
  &amp;lt;head&amp;gt;
    &amp;lt;meta charset="utf-8" /&amp;gt;
    &amp;lt;title&amp;gt;Grid&amp;lt;/title&amp;gt;
    &amp;lt;link rel="stylesheet" type="text/css" href="./styles.css" /&amp;gt;
  &amp;lt;/head&amp;gt;
  &amp;lt;body&amp;gt;
    &amp;lt;ul class="svh-grid"&amp;gt;
      &amp;lt;li&amp;gt;1&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;2&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;3&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;4&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;5&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;6&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;7&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;8&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;9&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;10&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;11&amp;lt;/li&amp;gt;
      &amp;lt;li&amp;gt;12&amp;lt;/li&amp;gt;
    &amp;lt;/ul&amp;gt;
  &amp;lt;/body&amp;gt;
&amp;lt;/html&amp;gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  CSS
&lt;/h3&gt;

&lt;p&gt;The stylesheet does all the work of rendering the list as a grid and setting the size of the items so that a certain number of rows will be visible without scrolling. Before you continue, it will help to be familiar with &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/Using_CSS_custom_properties" rel="noopener noreferrer"&gt;custom properties&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/calc" rel="noopener noreferrer"&gt;the &lt;code&gt;calc&lt;/code&gt; function&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_Grid_Layout" rel="noopener noreferrer"&gt;CSS grid&lt;/a&gt;.&lt;/p&gt;

&lt;h5&gt;
  
  
  styles.css
&lt;/h5&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;:root {
  --page-height: 100svh;
  --gap-size: 1svh;

  --column-count-portrait: 2;
  --row-count-portrait: 3;
  --gap-height-portrait: calc((var(--row-count-portrait) + 1) * var(--gap-size));
  --content-height-portrait: calc(var(--page-height) - var(--gap-height-portrait));
  --row-height-portrait: calc(var(--content-height-portrait) / var(--row-count-portrait));

  --column-count-landscape: 3;
  --row-count-landscape: 2;
  --gap-height-landscape: calc((var(--row-count-portrait) + 1) * var(--gap-size));
  --content-height-landscape: calc(var(--page-height) - var(--gap-height-landscape));
  --row-height-landscape: calc(var(--content-height-landscape) / var(--row-count-landscape));
}

html,
body {
  padding: 0;
  margin: 0;
  background-color: #FFF;
}

ul.svh-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--column-count-portrait), 1fr);
  grid-auto-rows: var(--row-height-portrait);
  grid-gap: var(--gap-size);
  padding: var(--gap-size);
  margin: 0;
}

ul.svh-grid &amp;gt; li {
  display: flex;
  justify-content: center;
  align-items: center;
  color: #FFF;
  background-color: #333;
  font-family: sans-serif;
  font-size: 10vmax;
}

@media (orientation: landscape) {
  ul.svh-grid {
    grid-template-columns: repeat(var(--column-count-landscape), 1fr);
    grid-auto-rows: var(--row-height-landscape);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We start by defining several custom properties. These aren't absolutely necessary, but they will help keep the CSS rules clean and readable.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;--page-height&lt;/code&gt; property is set to 100% of the small viewport height. (To reiterate, the small viewport encompasses all the space available to your application when the browser chrome is visible.)&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;--gap-size&lt;/code&gt; property represents the size of the gutters between grid items and the padding around the grid container. In this example, &lt;code&gt;--gap-size&lt;/code&gt; is set to 1% of the small viewport height, but it could have any value and use any units. If you want gutters to be &lt;code&gt;20px&lt;/code&gt; or &lt;code&gt;1rem&lt;/code&gt; on all devices, you're free to do so.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The grid properties are defined separately for portrait and landscape orientation:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;--column-count-portrait&lt;/code&gt;/&lt;code&gt;--column-count-landscape&lt;/code&gt;: The number of columns we want in the grid.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--row-count-portrait&lt;/code&gt;/&lt;code&gt;--row-count-landscape&lt;/code&gt;: The number of rows we want to render above the fold. The actual number of rows in the grid is determined by the html.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--gap-height-portrait&lt;/code&gt;/&lt;code&gt;--gap-height-landscape&lt;/code&gt;: The sum of all the gaps between rows that we want to render above the fold. The number of gaps will be one more than the number of rows. If we want three rows above the fold, there will be four gaps including the top and bottom.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--content-height-portrait&lt;/code&gt;/&lt;code&gt;--content-height-landscape&lt;/code&gt;: The total height available for content, calculated by subtracting the total gap height from the page height.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;--row-height-portrait&lt;/code&gt;/&lt;code&gt;--row-height-landscape&lt;/code&gt;: The row height, calculated by dividing the total content height by the number of rows we want to render above the fold.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Next, we use the custom properties in our CSS rules:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The &lt;code&gt;html, body&lt;/code&gt; rule is a simple reset to make sure the browser doesn’t apply default padding or margins that would affect the layout. (You might already have a reset like this in your stylesheet, in which case you might not need this.)
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;html,
body {
  padding: 0;
  margin: 0;
  background-color: #FFF;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ul.svh-grid&lt;/code&gt; rule unsets the default list styles and sets &lt;code&gt;display: grid&lt;/code&gt; to enable CSS grid layout. &lt;code&gt;grid-template-columns&lt;/code&gt; is set to render the list items in equal-width columns, with the number of columns defined by &lt;code&gt;--column-count-portrait&lt;/code&gt;. &lt;code&gt;grid-auto-rows&lt;/code&gt; is set to render as many rows as necessary, with the row height defined by &lt;code&gt;--row-height-portrait&lt;/code&gt;. The grid’s gap and padding are set to the value of the &lt;code&gt;--gap-size&lt;/code&gt; property. The margin is set to zero to remove any default margins that the browser might apply to the &lt;code&gt;ul&lt;/code&gt; element.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ul.svh-grid {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(var(--column-count-portrait), 1fr);
  grid-auto-rows: var(--row-height-portrait);
  grid-gap: var(--gap-size);
  padding: var(--gap-size);
  margin: 0;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;ul.svh-grid &amp;gt; li&lt;/code&gt; rule applies to the individual items in the grid. These styles affect the appearance of the list items and have no impact on the grid layout. Notice that the font size for the numbers is set using the &lt;code&gt;vmax&lt;/code&gt; unit. This makes the font size 10% of the maximum viewport dimension, which is the same in both portrait and landscape orientation. This is a simple way to set the font size proportionally to the viewport without having it shift on orientation change.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ul.svh-grid &amp;gt; li {
  display: flex;
  justify-content: center;
  align-items: center;
  color: #FFF;
  background-color: #333;
  font-family: sans-serif;
  font-size: 10vmax;
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, we use a media query to override the grid styles in landscape orientation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;@media (orientation: landscape) {
  ul.svh-grid {
    grid-template-columns: repeat(var(--column-count-landscape), 1fr);
    grid-auto-rows: var(--row-height-landscape);
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In portrait orientation, these styles give us a two column layout where three rows appear above the fold.&lt;/p&gt;

&lt;h5&gt;
  
  
  On page load or when scrolled to the top of the page:
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxnd35bnlz4gsfqf2xc6.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fjxnd35bnlz4gsfqf2xc6.png" alt="Two column grid layout in portrait orientation, scrolled to the top" width="750" height="1334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  When scrolled to the bottom of the page:
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ttgkokfcydrbsbxvgi4.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2F9ttgkokfcydrbsbxvgi4.png" alt="Two column grid layout in portrait orientation, scrolled to the bottom" width="750" height="1334"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In landscape orientation, these styles give us a three column layout where two rows appear above the fold:&lt;/p&gt;

&lt;h5&gt;
  
  
  On page load or when scrolled to the top of the page:
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7jx4n4wikht3uq4ppbo.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fm7jx4n4wikht3uq4ppbo.png" alt="Three column grid layout in landscape orientation, scrolled to the top" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  When scrolled to the bottom of the page:
&lt;/h5&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc3p0f7ejtvp1mknecsd.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fsc3p0f7ejtvp1mknecsd.png" alt="Three column grid layout in landscape orientation, scrolled to the bottom" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Not Use the Dynamic Viewport?
&lt;/h2&gt;

&lt;p&gt;You might wonder why we didn't use the dynamic viewport height (&lt;code&gt;dvh&lt;/code&gt;) instead of the small viewport, which would have allowed us to optimize the size of the grid items to the height of the viewport regardless of whether or not the browser chrome is visible. As soon as the user scrolls down the page, the browser chrome transitions offscreen and the dynamic viewport compensates by switching from the small viewport to the large viewport. If the layout is based on the dynamic viewport, the grid items will increase in height when that transition occurs. In addition to that problem, the CSS specification doesn’t require that the viewport transition animates at a full 60fps, likely because that would be computationally expensive in many scenarios. Some browsers perform the transition quite abruptly which causes a visible shift in the layout. In this case, using the small viewport height produces a more natural feeling result when scrolling. The behaviour will vary depending on the OS, browser and individual use cases, though, so experiment with the different viewports to find what works best for you. (If you want to see how the dynamic viewport works in this scenario, simply change the &lt;code&gt;--page-height&lt;/code&gt; property to &lt;code&gt;1dvh&lt;/code&gt;.)&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>css</category>
    </item>
    <item>
      <title>Building a Custom YAML Loader for Webpack</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Wed, 04 Jan 2023 14:45:00 +0000</pubDate>
      <link>https://dev.to/redbit/building-a-custom-yaml-loader-for-webpack-45b3</link>
      <guid>https://dev.to/redbit/building-a-custom-yaml-loader-for-webpack-45b3</guid>
      <description>&lt;p&gt;One of our internal tools at &lt;a href="https://redbitdev.com" rel="noopener noreferrer"&gt;RedBit&lt;/a&gt; uses &lt;a href="https://en.wikipedia.org/wiki/YAML" rel="noopener noreferrer"&gt;yaml&lt;/a&gt; to provide structured data. We chose yaml because it's more human-friendly than json, and the data in question is created and modified by humans during development. Applications need to consume the data as JavaScript objects, so we need to convert it from yaml to json. We could do the conversion at runtime but that would affect performance, possibly to the point of degrading the user experience. Instead, we chose to convert the data during the build process, which is controlled by &lt;a href="https://webpack.js.org/" rel="noopener noreferrer"&gt;webpack&lt;/a&gt;. This required a custom &lt;a href="https://webpack.js.org/api/loaders/" rel="noopener noreferrer"&gt;webpack loader&lt;/a&gt;. I won't describe the implementation of our internal tool in detail because it wouldn't be particularly helpful or relevant. Instead, I'll show you how to build a simple yaml loader that you can modify to suit your own needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Loader
&lt;/h2&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// yaml-loader.js

const { getOptions } = require('loader-utils');
const validate = require('schema-utils');
const yaml = require('js-yaml');

const loaderOptionsSchema = {
  type: 'object',
  properties: {
    commonjs: {
      description: 'Use CommonJS exports (default: false)',
      type: 'boolean',
    },
  },
};

module.exports = (loaderContext, source) =&amp;gt; {
  const callback = loaderContext.async();
  const loaderOptions = getOptions(loaderContext) || {};

  validate(loaderOptionsSchema, loaderOptions, {
    name: 'yaml-loader',
    baseDataPath: 'options',
  });

  const { commonjs = false } = loaderOptions;

  try {
    const data = yaml.load(source);

    // At this point you may perform additional validations
    // and transformations on your data...

    const json = JSON.stringify(data, null, 2);
    const ecmaFileContents = `export default ${json};`;
    const cjsFileContents = `module.exports = ${json};`;
    const fileContents = commonjs ? cjsFileContents : ecmaFileContents;
    callback(null, fileContents);
  } catch (error) {
    callback(error);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We begin by defining a schema for the loader options so we can validate them with &lt;a href="https://github.com/webpack/schema-utils" rel="noopener noreferrer"&gt;schema-utils&lt;/a&gt;. The schema is an object that describes properties that you can set when you integrate the loader in your webpack config:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const loaderOptionsSchema = {
  type: 'object',
  properties: {
    commonjs: {
      description: 'Use CommonJS exports (default: false)',
      type: 'boolean',
    },
  },
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;In this case, we have one option, &lt;code&gt;commonjs&lt;/code&gt;, which is a boolean. If the &lt;code&gt;commonjs&lt;/code&gt; option is &lt;code&gt;true&lt;/code&gt;, the loader will generate JavaScript files that use CommonJS modules (e.g. &lt;code&gt;module.exports&lt;/code&gt;). Otherwise, the loader will use ECMA modules (e.g. &lt;code&gt;export default&lt;/code&gt;). You will likely want ECMA modules in most modern web applications, but the option gives you some flexibility to work in other environments. (Note that the loader itself is written as a CommonJS module because it always runs in Node. Using CommonJS avoids some compatibility issues with ECMA modules in older versions of Node.)&lt;/p&gt;

&lt;p&gt;Next, we have the loader function itself:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;module.exports = (loaderContext, source) =&amp;gt; {
  const callback = loaderContext.async();
  const loaderOptions = getOptions(loaderContext) || {};

  validate(loaderOptionsSchema, loaderOptions, {
    name: 'yaml-loader',
    baseDataPath: 'options',
  });

  const { commonjs = false } = loaderOptions;

  try {
    const data = yaml.load(source);

    // At this point you may perform additional validations
    // and transformations on your data...

    const json = JSON.stringify(data, null, 2);
    const ecmaFileContents = `export default ${json};`;
    const cjsFileContents = `module.exports = ${json};`;
    const fileContents = commonjs ? cjsFileContents : ecmaFileContents;
    callback(null, fileContents);
  } catch (error) {
    callback(error);
  }
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This function accepts two arguments. &lt;code&gt;loaderContext&lt;/code&gt; is the context in which the loader runs. We'll use this to obtain some information about the loader, including the options. &lt;code&gt;source&lt;/code&gt; is the contents of the input file as a string (a yaml string in this instance). The function performs the following tasks:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Call &lt;code&gt;loaderContext.async()&lt;/code&gt; to tell the loader that the process will run asynchronously. &lt;code&gt;loaderContext.async()&lt;/code&gt; returns a callback that we'll use to pass the results of the process back to the loader.&lt;/li&gt;
&lt;li&gt;Obtain the loader options by calling &lt;code&gt;getOptions(loaderContext)&lt;/code&gt;, which is a function provided by &lt;a href="https://github.com/webpack/loader-utils" rel="noopener noreferrer"&gt;loader-utils&lt;/a&gt;. We default the return value of &lt;code&gt;getOptions&lt;/code&gt; to an empty object literal in case the webpack config doesn't include the options hash.&lt;/li&gt;
&lt;li&gt;Validate the loader options against the schema we created earlier. This will throw an error if the options aren't specified correctly in the webpack config. Unpack the options, if desired.&lt;/li&gt;
&lt;li&gt;Parse the &lt;code&gt;source&lt;/code&gt; string. We're using &lt;a href="https://github.com/nodeca/js-yaml" rel="noopener noreferrer"&gt;js-yaml&lt;/a&gt; for this.&lt;/li&gt;
&lt;li&gt;At this point the data is parsed and you can perform additional validations and transformations on it.&lt;/li&gt;
&lt;li&gt;Json-serialize the data using &lt;code&gt;JSON.stringify()&lt;/code&gt;. Set the indentation according to your preferences (2 spaces in this case).&lt;/li&gt;
&lt;li&gt;Create the file contents for ECMA and CommonJS modules by appending the serialized json string to the export statement.&lt;/li&gt;
&lt;li&gt;Execute the callback with the appropriate file content string based on the &lt;code&gt;commonjs&lt;/code&gt; option.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The result of this process will be that you will be able to import a yaml file in your JavaScript code, and its contents will be made available as a JavaScript module instead of a yaml string.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import data from './data.yaml';

for (key in data) {
  console.log(`The value for key '${key}' is ${data[key]}`);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h2&gt;
  
  
  Integration
&lt;/h2&gt;

&lt;p&gt;Integration with webpack is similar to any other loader. Add a new rule to the &lt;code&gt;module.rules&lt;/code&gt; array in your webpack config. The rule should &lt;code&gt;test&lt;/code&gt; files for the &lt;code&gt;.yaml&lt;/code&gt; file extension, &lt;code&gt;exclude&lt;/code&gt; any files in the &lt;code&gt;node_modules&lt;/code&gt; directory and &lt;code&gt;use&lt;/code&gt; your custom yaml loader:&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// webpack.config.js

module: {
  rules: [
    {
      test: /\.yaml$/,
      exclude: /node_modules/,
      use: {
        loader: path.resolve('./yaml-loader'),
        options: {
          commonjs: false,
        },
      },
    },
  ],
},
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;You can use this loader for simple yaml files, or as a starting point for a more complex loader of your own.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>webpack</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Improving Render Performance in React</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Mon, 21 Nov 2022 14:00:00 +0000</pubDate>
      <link>https://dev.to/redbit/improving-render-performance-in-react-46ij</link>
      <guid>https://dev.to/redbit/improving-render-performance-in-react-46ij</guid>
      <description>&lt;h1&gt;
  
  
  Improving Render Performance in React
&lt;/h1&gt;

&lt;p&gt;Tony Wallace, November 3 2022&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Note: This article begins with a discussion of class components in React. By this point in time you've probably migrated to functional components and hooks. That's great! Functional components and hooks are the way forward for React and we'll discuss them later in this article. We're starting with class components to give some historical perspective, and because you should try to be familiar with both styles. A lot of React apps have been written using class components and it's likely that you'll have to maintain one at some point in your career.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conditional rendering in class components
&lt;/h2&gt;

&lt;p&gt;If you have worked with class components in React, you may have come across the &lt;code&gt;shouldComponentUpdate&lt;/code&gt; lifecycle method. This method runs before each render and accepts the next props and state objects. You can use &lt;code&gt;shouldComponentUpdate&lt;/code&gt; to compare the next props and state to the current props and state, and decide whether to allow the component to render. (&lt;code&gt;shouldComponentUpdate&lt;/code&gt; returns &lt;code&gt;true&lt;/code&gt; by default if you don't override it in your component class, which always allows the component to render.)&lt;/p&gt;

&lt;p&gt;In the following example, we have a user profile component that renders a list of the user's skills. Each skill has a name and a description. The description is served as &lt;a href="https://www.markdownguide.org" rel="noopener noreferrer"&gt;markdown&lt;/a&gt; and we need to parse it to HTML before we render it. We can do this with a markdown parser (&lt;a href="https://github.com/markedjs/marked" rel="noopener noreferrer"&gt;marked&lt;/a&gt;, in this case) and use &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; to inject the HTML into a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element. Note that using &lt;code&gt;dangerouslySetInnerHTML&lt;/code&gt; may leave your site vulnerable to &lt;a href="https://owasp.org/www-community/attacks/xss/" rel="noopener noreferrer"&gt;XSS&lt;/a&gt; attacks. In order to mitigate this risk, we need to sanitize the HTML before we inject it. We'll use &lt;a href="https://github.com/cure53/DOMPurify" rel="noopener noreferrer"&gt;DOMPurify&lt;/a&gt; for that task.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';
import marked from 'marked';
import * as DOMPurify from 'dompurify';

class UserProfile extends Component {
  constructor (props) {
    super(props);
  }

  render () {
    const { userId, name, skills = [] } = this.props;

    return (
      &amp;lt;div className="user-profile"&amp;gt;
        &amp;lt;h1&amp;gt;{name} ({userId})&amp;lt;/h1&amp;gt;
        &amp;lt;h3&amp;gt;Skills&amp;lt;/h3&amp;gt;
        {skills.map((skill) =&amp;gt; (
          &amp;lt;div className="skill"&amp;gt;
            &amp;lt;h5&amp;gt;{skill.name}&amp;lt;/h5&amp;gt;
            &amp;lt;div dangerouslySetInnerHTML={{
              __html: DOMPurify.sanitize(marked.parse(skill.description)),
            }} /&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To recap, our process is:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Parse the skill description markdown to HTML&lt;/li&gt;
&lt;li&gt;Sanitize the HTML&lt;/li&gt;
&lt;li&gt;Inject the HTML into a &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt; element&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;If we think through this process in the context of the component render cycle, we can see how it could have a negative effect on performance:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Perform two potentially expensive tasks in series (markdown parsing and HTML sanitization)&lt;/li&gt;
&lt;li&gt;Perform those tasks in a loop, which is also executed in series&lt;/li&gt;
&lt;li&gt;Repeat every time the component renders&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We can't do much about the first and second issues — we have to parse the markdown, sanitize the resulting HTML and render it — but we can avoid the third issue. Why render if nothing has changed? Here is an updated version of the previous example that uses &lt;code&gt;shouldComponentUpdate&lt;/code&gt; to block the component from rendering unless the &lt;code&gt;userId&lt;/code&gt; prop has changed:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';
import marked from 'marked';
import * as DOMPurify from 'dompurify';

class UserProfile extends Component {
  constructor (props) {
    super(props);
  }

  shouldComponentUpdate (nextProps) {
    return nextProps.userId !== this.props.userId;
  }

  render () {
    const { userId, name, skills = [] } = this.props;

    return (
      &amp;lt;div className="user-profile"&amp;gt;
        &amp;lt;h1&amp;gt;{name} ({userId})&amp;lt;/h1&amp;gt;
        &amp;lt;h3&amp;gt;Skills&amp;lt;/h3&amp;gt;
        {skills.map((skill) =&amp;gt; (
          &amp;lt;div className="skill"&amp;gt;
            &amp;lt;h5&amp;gt;{skill.name}&amp;lt;/h5&amp;gt;
            &amp;lt;div dangerouslySetInnerHTML={{
              __html: DOMPurify.sanitize(marked.parse(skill.description)),
            }} /&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;We're comparing the &lt;code&gt;userId&lt;/code&gt; prop because it's a simple equality comparison that will tell us when the user has changed. You might be tempted to try to perform a deep equality comparison of the &lt;code&gt;skills&lt;/code&gt; array instead, but that will likely create a worse performance issue than the one we're trying to solve.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;shouldComponentUpdate&lt;/code&gt; is &lt;a href="https://reactjs.org/docs/react-component.html#shouldcomponentupdate" rel="noopener noreferrer"&gt;intended to be used for performance optimization&lt;/a&gt; but should be used carefully and sparingly. Blocking a component from rendering can cause bugs in descendant components that can be difficult to fix, and there are usually better ways to improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reducing the rendering workload
&lt;/h2&gt;

&lt;p&gt;In the previous example, we tried to mitigate a potential performance problem by blocking a component from rendering. That was the wrong solution to take because it didn't address the real problem. The real problem isn't that we're allowing the component to render when it doesn't have to, it's that &lt;em&gt;we're doing too much work in the render cycle.&lt;/em&gt; Therefore, the correct solution is not to block rendering entirely, but to reduce the amount of work we do while rendering.&lt;/p&gt;

&lt;h3&gt;
  
  
  Option 1: Store preprocessed data on state
&lt;/h3&gt;

&lt;p&gt;One way of reducing the workload is to run the expensive process only when inputs change and store the results on state. This is simple and doesn't interfere with the render cycle. It works equally well in both class and functional components.&lt;/p&gt;

&lt;h5&gt;
  
  
  Class component
&lt;/h5&gt;

&lt;p&gt;The following example updates our previous examples to preprocess the user's skills and store them on state, instead of reading directly from props in the render cycle. The preprocessing logic has been moved into the &lt;code&gt;processSkills&lt;/code&gt; function, which is called in two places: &lt;code&gt;componentDidMount&lt;/code&gt;, where it will run before the initial render; and &lt;code&gt;componentDidUpdate&lt;/code&gt;, where it will run when the value of the &lt;code&gt;userId&lt;/code&gt; prop changes. If the component renders for any other reason, it will continue to use the preprocessed data from state at no additional cost.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { Component } from 'react';
import marked from 'marked';
import * as DOMPurify from 'dompurify';

const processSkills = (skills = []) =&amp;gt; {
  return skills.map((skill) =&amp;gt; ({
        ...skill,
        htmlDescription: DOMPurify.sanitize(marked.parse(skill.description)),
      }));
};

class UserProfile extends Component {
  constructor (props) {
    super(props);

    this.state = {
      processedSkills: [],
    };
  }

  componentDidMount () {
    this.setState({
      processedSkills: processSkills(this.props.skills),
    });
  }

  componentDidUpdate (prevProps) {
    if (this.props.userId !== prevProps.userId) {
      this.setState({
        processedSkills: processSkills(this.props.skills),
      });
    }
  }

  render () {
    const { userId, name } = this.props;
    const { processedSkills = [] } = this.state;

    return (
      &amp;lt;div className="user-profile"&amp;gt;
        &amp;lt;h1&amp;gt;{name} ({userId})&amp;lt;/h1&amp;gt;
        &amp;lt;h3&amp;gt;Skills&amp;lt;/h3&amp;gt;
        {processedSkills.map((skill) =&amp;gt; (
          &amp;lt;div className="skill"&amp;gt;
            &amp;lt;h5&amp;gt;{skill.name}&amp;lt;/h5&amp;gt;
            &amp;lt;div dangerouslySetInnerHTML={{
              __html: skill.htmlDescription,
            }} /&amp;gt;
          &amp;lt;/div&amp;gt;
        ))}
      &amp;lt;/div&amp;gt;
    );
  }
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h5&gt;
  
  
  Functional component (hooks)
&lt;/h5&gt;

&lt;p&gt;The functional component produces the same result as the class component, but is more concise. The &lt;code&gt;useEffect&lt;/code&gt; hook calls &lt;code&gt;processSkills&lt;/code&gt; with the &lt;code&gt;skills&lt;/code&gt; array from props when the value of the &lt;code&gt;userId&lt;/code&gt; prop changes. The resulting array is stored on state and used to render the skills list.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState, useEffect } from 'react';
import marked from 'marked';
import * as DOMPurify from 'dompurify';

const processSkills = (skills = []) =&amp;gt; {
  return skills.map((skill) =&amp;gt; ({
        ...skill,
        htmlDescription: DOMPurify.sanitize(marked.parse(skill.description)),
      }));
};

const UserProfile = (props) =&amp;gt; {
  const { userId, name, skills = [] } = this.props;
  const [ processedSkills, setProcessedSkills ] = useState([]);

  useEffect(
    () =&amp;gt; setProcessedSkills(processSkills(skills)),
    [userId]
  );

      return (
        &amp;lt;div className="user-profile"&amp;gt;
          &amp;lt;h1&amp;gt;{name} ({userId})&amp;lt;/h1&amp;gt;
          &amp;lt;h3&amp;gt;Skills&amp;lt;/h3&amp;gt;
          {processedSkills.map((skill) =&amp;gt; (
        &amp;lt;div className="skill"&amp;gt;
          &amp;lt;h5&amp;gt;{skill.name}&amp;lt;/h5&amp;gt;
          &amp;lt;div dangerouslySetInnerHTML={{
            __html: skill.htmlDescription,
          }} /&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
        &amp;lt;/div&amp;gt;
      );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Option 2: Memoize preprocessed data
&lt;/h3&gt;

&lt;p&gt;Another option is to memoize (not &lt;em&gt;memorize&lt;/em&gt;) the results of the process. Memoization is a form of in-memory caching. We're only going to discuss this in the context of functional components where we can use the &lt;code&gt;useMemo&lt;/code&gt; hook provided by React, but you may be able to achieve similar results in a class component using a &lt;a href="https://github.com/alexreardon/memoize-one" rel="noopener noreferrer"&gt;third-party helper&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In this example, the &lt;code&gt;useMemo&lt;/code&gt; hook is called on every render. On the initial render and any time the &lt;code&gt;userId&lt;/code&gt; prop is updated, &lt;code&gt;useMemo&lt;/code&gt; returns the result of calling &lt;code&gt;processSkills&lt;/code&gt; with the &lt;code&gt;skills&lt;/code&gt; array. If the &lt;code&gt;userId&lt;/code&gt; prop hasn't changed since the last render, &lt;code&gt;useMemo&lt;/code&gt; returns the previously cached result.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useMemo } from 'react';
import marked from 'marked';
import * as DOMPurify from 'dompurify';

const processSkills = (skills = []) =&amp;gt; {
  return skills.map((skill) =&amp;gt; ({
        ...skill,
        htmlDescription: DOMPurify.sanitize(marked.parse(skill.description)),
      }));
};

const UserProfile = (props) =&amp;gt; {
  const { userId, name, skills = [] } = this.props;

  const processedSkills = useMemo(
    () =&amp;gt; processSkills(skills),
    [userId]
  );

      return (
        &amp;lt;div className="user-profile"&amp;gt;
          &amp;lt;h1&amp;gt;{name} ({userId})&amp;lt;/h1&amp;gt;
          &amp;lt;h3&amp;gt;Skills&amp;lt;/h3&amp;gt;
          {processedSkills.map((skill) =&amp;gt; (
        &amp;lt;div className="skill"&amp;gt;
          &amp;lt;h5&amp;gt;{skill.name}&amp;lt;/h5&amp;gt;
          &amp;lt;div dangerouslySetInnerHTML={{
            __html: skill.htmlDescription,
          }} /&amp;gt;
        &amp;lt;/div&amp;gt;
      ))}
        &amp;lt;/div&amp;gt;
      );
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Which option should I choose?
&lt;/h3&gt;

&lt;p&gt;If you're working with class components, I suggest preprocessing and storing data on state unless you think you have a strong use case for integrating a memoization helper (e.g. you need memoization throughout your app, not just in one or two places).&lt;/p&gt;

&lt;p&gt;In functional components, memoization offers the same benefit as preprocessing and storing the result on state without having to maintain state. However, it isn't guaranteed to be predictable. &lt;a href="https://reactjs.org/docs/hooks-reference.html#usememo" rel="noopener noreferrer"&gt;From the React docs&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;You may rely on useMemo as a performance optimization, not as a semantic guarantee.&lt;/strong&gt; In the future, React may choose to “forget” some previously memoized values and recalculate them on next render, e.g. to free memory for offscreen components. Write your code so that it still works without useMemo — and then add it to optimize performance.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Storing data on state may be a better choice if the predictability of your performance optimizations is critical to your application. In many, if not most cases it won't be critical, and memoization will likely be the cleanest and simplest way to improve render performance.&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>javascript</category>
    </item>
    <item>
      <title>Conditional Logging to the JavaScript Console</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Mon, 14 Nov 2022 14:00:00 +0000</pubDate>
      <link>https://dev.to/redbit/conditional-logging-to-the-javascript-console-2bl9</link>
      <guid>https://dev.to/redbit/conditional-logging-to-the-javascript-console-2bl9</guid>
      <description>&lt;p&gt;The JavaScript console is a very useful debugging tool, but it can also introduce a very simple security risk into your application: &lt;code&gt;console&lt;/code&gt; method calls that aren't removed before code is deployed can leak sensitive information to the browser. That's why many developers use a &lt;a href="https://eslint.org/" rel="noopener noreferrer"&gt;linter&lt;/a&gt; to warn about &lt;code&gt;console&lt;/code&gt; method calls and other issues. (On RedBit's web team, we use &lt;a href="https://typicode.github.io/husky" rel="noopener noreferrer"&gt;husky&lt;/a&gt; to run the linter on a pre-commit hook so that &lt;code&gt;console&lt;/code&gt; method calls are never committed by accident. We've found that many small bugs can be avoided by making linting mandatory before committing code.)&lt;/p&gt;

&lt;p&gt;The problem is that there are times when you want to leave &lt;code&gt;console&lt;/code&gt; method calls in place. You can use &lt;code&gt;eslint-disable&lt;/code&gt; comments to bypass the linter, but you still run the risk of leaking sensitive information if you forget to clean up before you deploy. You could also wrap your &lt;code&gt;console&lt;/code&gt; method calls in a condition that checks a debug or environment flag and only executes the calls in development, but that's a bit awkward. It's also easy to forget so your users' security will depend on your memory. That certainly isn't a good strategy given that we all make mistakes from time to time.&lt;/p&gt;

&lt;p&gt;A better solution would be to integrate the debug or environment condition into the console itself. We can do this by replacing the browser's &lt;code&gt;window.console&lt;/code&gt; object with a modified version of the same. &lt;em&gt;Example 1&lt;/em&gt; shows the function that does this.&lt;/p&gt;

&lt;h5&gt;
  
  
  Example 1
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// console.js

export const createDebugConsole = (consoleObj, options = {}) =&amp;gt; {
  const nextConsoleObj = { ...consoleObj };
  const { enabled = true } = options;

  for (let key in nextConsoleObj) {
    /* eslint-disable no-prototype-builtins */
    if (
      nextConsoleObj.hasOwnProperty(key) &amp;amp;&amp;amp;
      typeof nextConsoleObj[key] === 'function'
    ) {
      const func = nextConsoleObj[key];
      nextConsoleObj[key] = function () {
        if (enabled) {
          func.apply(nextConsoleObj, arguments);
        }
      };
    }
    /* eslint-enable no-prototype-builtins */
  }

  return nextConsoleObj;
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice that the &lt;code&gt;createDebugConsole&lt;/code&gt; accepts the console object as an argument, rather than assuming it to be in scope. The function is designed this way for two reasons. First, it eliminates a dependency on the global scope which makes the function easier to unit test. Second, it makes it possible to use the function in non-browser environments that use a browser-like &lt;code&gt;console&lt;/code&gt; object that may not be attached to the &lt;code&gt;window&lt;/code&gt; global.&lt;/p&gt;

&lt;p&gt;Let's examine this function line by line:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Accept the console object and a hash of options as arguments.&lt;/li&gt;
&lt;li&gt;Make a shallow copy of the console object as &lt;code&gt;nextConsoleObj&lt;/code&gt;, to avoid modifying the original.&lt;/li&gt;
&lt;li&gt;Destructure the &lt;code&gt;enabled&lt;/code&gt; boolean from the options hash. The default value is &lt;code&gt;true&lt;/code&gt;, which means that the new console will allow logging unless you pass &lt;code&gt;{ enabled: false }&lt;/code&gt; in the options hash.&lt;/li&gt;
&lt;li&gt;Enumerate the properties of the copied console object. For each key that is owned by the object (i.e. not inherited) and whose value is a function, take a reference to the value (&lt;code&gt;func&lt;/code&gt;) and then replace it with a new function. If the &lt;code&gt;enabled&lt;/code&gt; option is &lt;code&gt;true&lt;/code&gt;, the new function calls &lt;code&gt;func.apply&lt;/code&gt; with the new console object and the new function's arguments. (Note that we're using the traditional &lt;code&gt;function&lt;/code&gt; declaration here, rather than an ES6 arrow function, to ensure that we have access to the &lt;code&gt;arguments&lt;/code&gt; array.) If the &lt;code&gt;enabled&lt;/code&gt; option is &lt;code&gt;false&lt;/code&gt;, the new function does nothing. Any additional properties of the console object that are not functions will be left unchanged.&lt;/li&gt;
&lt;li&gt;Return the new console object.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;(Depending on your linter rules, you may or may not need the &lt;code&gt;/* eslint-disable no-prototype-builtins */&lt;/code&gt; and &lt;code&gt;/* eslint-enable no-prototype-builtins */&lt;/code&gt; comments.)&lt;/p&gt;

&lt;h2&gt;
  
  
  Integration
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Example 2&lt;/em&gt; shows how to use the function to replace the browser's default console with our new debug console. In this case, we're using &lt;code&gt;process.env.NODE_ENV&lt;/code&gt; to enable logging in non-production builds. (This assumes that your build process replaces &lt;code&gt;process.env.NODE_ENV&lt;/code&gt; with the value of the &lt;code&gt;NODE_ENV&lt;/code&gt; environment variable.) You could also set the &lt;code&gt;enabled&lt;/code&gt; option based on a value from a &lt;a href="https://github.com/motdotla/dotenv" rel="noopener noreferrer"&gt;dotenv&lt;/a&gt; file or any other source of configuration.&lt;/p&gt;

&lt;h5&gt;
  
  
  Example 2
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// index.js

import { createDebugConsole } from './console';

window.console = createDebugConsole(window.console, {
  enabled: process.env.NODE_ENV !== 'production'
});

console.log("This won't log in production!");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Call &lt;code&gt;createDebugConsole&lt;/code&gt; once in your application's main &lt;code&gt;index.js&lt;/code&gt; file or wherever you do other setup work. You can now use the global &lt;code&gt;console&lt;/code&gt; object normally. If the condition that sets the &lt;code&gt;enabled&lt;/code&gt; value is &lt;code&gt;true&lt;/code&gt;, the &lt;code&gt;console&lt;/code&gt; methods will behave normally. It it's &lt;code&gt;false&lt;/code&gt; the methods will return without doing anything. You can now be more confident that your production applications won't leak data to the console.&lt;/p&gt;

&lt;p&gt;You could also assign the return value of &lt;code&gt;createDebugConsole&lt;/code&gt; to a new constant in case you need to leave the &lt;code&gt;window.console&lt;/code&gt; global untouched, or if you just don't want to use the global console, as shown in &lt;em&gt;Example 3:&lt;/em&gt;&lt;/p&gt;

&lt;h5&gt;
  
  
  Example 3
&lt;/h5&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createDebugConsole } from './console';

const myConsole = createDebugConsole(window.console, {
  enabled: process.env.NODE_ENV !== 'production'
});

myConsole.log("This won't log in production!");
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The approach you take should depend on your application and personal preferences. I prefer to use the &lt;code&gt;window.console&lt;/code&gt; global for two reasons:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;It allows me to use the console without having to import anything&lt;/li&gt;
&lt;li&gt;The linter will still complain if I leave &lt;code&gt;console&lt;/code&gt; method calls in my code without explicitly allowing them with &lt;code&gt;eslint-disable no-console&lt;/code&gt; comments. This helps reduce unnecessary logging.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Testing
&lt;/h2&gt;

&lt;p&gt;The console created by &lt;code&gt;createDebugConsole&lt;/code&gt; will not be active in unit tests, so you won't be able to use it to prevent logging in your test environment. If you use &lt;a href="https://jestjs.io/" rel="noopener noreferrer"&gt;jest&lt;/a&gt; you can &lt;a href="https://jestjs.io/docs/cli#--silent" rel="noopener noreferrer"&gt;silence &lt;code&gt;console&lt;/code&gt; methods with the jest CLI.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is a short &lt;code&gt;jest&lt;/code&gt; suite that you can use to test &lt;code&gt;createDebugConsole&lt;/code&gt;:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import { createDebugConsole } from './console';

const getMockConsole = () =&amp;gt; ({
  assert: jest.fn(),
  clear: jest.fn(),
  count: jest.fn(),
  countReset: jest.fn(),
  debug: jest.fn(),
  dir: jest.fn(),
  dirxml: jest.fn(),
  error: jest.fn(),
  group: jest.fn(),
  groupCollapsed: jest.fn(),
  groupEnd: jest.fn(),
  info: jest.fn(),
  log: jest.fn(),
  profile: jest.fn(),
  profileEnd: jest.fn(),
  table: jest.fn(),
  time: jest.fn(),
  timeEnd: jest.fn(),
  timeLog: jest.fn(),
  timeStamp: jest.fn(),
  trace: jest.fn(),
  warn: jest.fn(),
});

test('createDebugConsole returns a copy of the input console', () =&amp;gt; {
  const consoleObj = getMockConsole();
  const nextConsoleObj = createDebugConsole(consoleObj);

  expect(consoleObj === nextConsoleObj).toBe(false);

  for (let key in consoleObj) {
    // eslint-disable-next-line no-prototype-builtins
    if (consoleObj.hasOwnProperty(key)) {
      expect(consoleObj[key] === nextConsoleObj[key]).toBe(false);
    }
  }
});

test('When a function is called on the debug console returned by createDebugConsole, the same function is called on the original console with the same arguments if the debug console is enabled', () =&amp;gt; {
  const consoleObj = getMockConsole();
  const nextConsoleObj = createDebugConsole(consoleObj, { enabled: true });
  const args = ['test', 'test2', 'test3'];

  for (let key in nextConsoleObj) {
    // eslint-disable-next-line no-prototype-builtins
    if (nextConsoleObj.hasOwnProperty(key)) {
      nextConsoleObj[key](...args);
      expect(consoleObj[key]).toHaveBeenCalledTimes(1);
      expect(consoleObj[key]).toHaveBeenCalledWith(...args);
    }
  }
});

test('When a function is called on the debug console returned by createDebugConsole, the same function is not called on the original console if the debug console is disabled', () =&amp;gt; {
  const consoleObj = getMockConsole();
  const nextConsoleObj = createDebugConsole(consoleObj, { enabled: false });
  const args = ['test', 'test2', 'test3'];

  for (let key in nextConsoleObj) {
    // eslint-disable-next-line no-prototype-builtins
    if (nextConsoleObj.hasOwnProperty(key)) {
      nextConsoleObj[key](...args);
      expect(consoleObj[key]).not.toHaveBeenCalled();
    }
  }
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

</description>
      <category>webdev</category>
      <category>javascript</category>
    </item>
    <item>
      <title>SQL Tip: Self-Referential Tables</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Wed, 09 Nov 2022 13:48:52 +0000</pubDate>
      <link>https://dev.to/redbit/sql-tip-self-referential-tables-52fj</link>
      <guid>https://dev.to/redbit/sql-tip-self-referential-tables-52fj</guid>
      <description>&lt;p&gt;&lt;em&gt;Note: The examples in this post are written for MSSQL. Syntax may differ for other SQL variants.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;I was recently tasked with designing a SQL database for a new app. One of the requirements was to be able to store a category tree of unknown complexity and depth. It wasn't sufficient to have categories and subcategories; subcategories could also have subcategories, which could have subcategories, and so on. Each branch of the tree could have a different structure and continue to a different depth. An example tree would look something like this:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Category 1

&lt;ul&gt;
&lt;li&gt;Category 1.1&lt;/li&gt;
&lt;li&gt;Category 1.2&lt;/li&gt;
&lt;li&gt;Category 1.2.1&lt;/li&gt;
&lt;li&gt;Category 1.2.2&lt;/li&gt;
&lt;li&gt;Category 1.3&lt;/li&gt;
&lt;li&gt;Category 1.3.1

&lt;ul&gt;
&lt;li&gt;Category 1.3.1.1&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category 1.3.2&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;Category 2

&lt;ul&gt;
&lt;li&gt;Category 2.1&lt;/li&gt;
&lt;li&gt;Category 2.1.1

&lt;ul&gt;
&lt;li&gt;Category 2.1.1.1&lt;/li&gt;
&lt;li&gt;Category 2.1.1.2&lt;/li&gt;
&lt;li&gt;Category 2.1.1.2.1&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Category 2.2&lt;/li&gt;

&lt;li&gt;Category 2.2.1&lt;/li&gt;

&lt;li&gt;Category 2.2.2&lt;/li&gt;

&lt;li&gt;Category 2.2.3&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;It was clearly not going to be possible to satisfy these requirements with simple &lt;code&gt;category&lt;/code&gt; and &lt;code&gt;subcategory&lt;/code&gt; tables. I needed to be able to describe all the category relationships in one self-referential table.&lt;/p&gt;

&lt;h2&gt;
  
  
  Designing the self-referential table
&lt;/h2&gt;

&lt;p&gt;Relationships in a self-referential table are defined by two fields:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;code&gt;id&lt;/code&gt; (primary key, uniqueidentifier, not nullable)&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;parentId&lt;/code&gt; (foreign key, uniqueidentifier, nullable)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Note that &lt;code&gt;parentId&lt;/code&gt; is a foreign key that &lt;em&gt;references the same table.&lt;/em&gt; If your table is called &lt;code&gt;category&lt;/code&gt;, &lt;code&gt;category.parentId&lt;/code&gt; has a foreign key constraint to &lt;code&gt;category.id&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;(You can also declare any other fields you might need to store the category name and other information. Those aren't relevant to this tip.)&lt;/p&gt;

&lt;p&gt;When you insert a row into the table, you can set &lt;code&gt;parentId&lt;/code&gt; to &lt;code&gt;null&lt;/code&gt; to put the row at the top of the tree, or set &lt;code&gt;parentId&lt;/code&gt; to the &lt;code&gt;id&lt;/code&gt; of any other category to make the row a subcategory.&lt;/p&gt;

&lt;h2&gt;
  
  
  Selecting branches of the tree
&lt;/h2&gt;

&lt;p&gt;Creating the tree is simple enough, but how do you select only the rows that are descendants of a given category id? For that, we can use a recursive common-table expression (recursive CTE).&lt;/p&gt;

&lt;p&gt;The recursive CTE creates a virtual table (&lt;code&gt;[tree]&lt;/code&gt;) that is the union of the rows that have a given parent, the rows that have that first set of rows as parents, the rows that have that next set of rows as parents, and so on until there are no more descendent rows to fetch. We then select all rows from the virtual table:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @id UNIQUEIDENTIFIER;
SET @id = '&amp;lt;id of the category whose descendants you want to select&amp;gt;';

WITH [tree] AS (
    SELECT parent.* FROM [category] parent WHERE parent.id = @id
    UNION ALL
    SELECT child.* FROM [tree]
    INNER JOIN [category] child ON child.parentId = [tree].id
)

SELECT * from [tree]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can modify the &lt;code&gt;SELECT&lt;/code&gt; statement to fetch only the bottom-most descendants of a given category, omitting the upper levels of the tree:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @id UNIQUEIDENTIFIER;
SET @id = '&amp;lt;id of the category whose descendants you want to select&amp;gt;';

WITH [tree] AS (
    SELECT parent.* FROM [category] parent WHERE parent.id = @id
    UNION ALL
    SELECT child.* FROM [tree]
    INNER JOIN [category] child ON child.parentId = [tree].id
)

SELECT * FROM [tree] WHERE NOT EXISTS (
    SELECT parentId FROM [category] WHERE [category].parentId = [tree].id
)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;We can also invert the CTE to fetch categories that are ancestors of a given subcategory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @id UNIQUEIDENTIFIER;
SET @id = '&amp;lt;id of the subcategory whose ancestors you want to select&amp;gt;';

WITH [tree] AS (
    SELECT child.* FROM [category] child WHERE child.id = @id
    UNION ALL
    SELECT parent.* FROM [tree]
    INNER JOIN [category] parent ON parent.id = [tree].parentId
)

SELECT * from [tree]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;By modifying the &lt;code&gt;SELECT&lt;/code&gt; statement, we can find the top-most ancestor of any subcategory:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;DECLARE @id UNIQUEIDENTIFIER;
SET @id = '&amp;lt;id of the subcategory whose ancestors you want to select&amp;gt;';

WITH [tree] AS (
    SELECT child.* FROM [category] child WHERE child.id = @id
    UNION ALL
    SELECT parent.* FROM [tree]
    INNER JOIN [category] parent ON parent.id = [tree].parentId
)

SELECT * from [tree] WHERE parentId IS NULL
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This combination of a self-referential table and recursive common-table expressions provided a great solution to my requirements. The model is straightforward, and easy to maintain, and the selection of entire branches can be performed in a single query.&lt;/p&gt;

</description>
      <category>sql</category>
      <category>programming</category>
    </item>
    <item>
      <title>Using Array.reduce With Objects</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Tue, 04 Jan 2022 14:23:48 +0000</pubDate>
      <link>https://dev.to/redbit/using-arrayreduce-with-objects-50jp</link>
      <guid>https://dev.to/redbit/using-arrayreduce-with-objects-50jp</guid>
      <description>&lt;p&gt;This article will demonstrate a few ways that you can use JavaScript's &lt;code&gt;Array.reduce&lt;/code&gt; method to transform objects.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Array.reduce primer
&lt;/h2&gt;

&lt;p&gt;Let's take a quick look at how &lt;code&gt;Array.reduce&lt;/code&gt; works. You can skip this if you're already familiar with it.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Array.reduce&lt;/code&gt; reduces an array down to a single value. The resulting value can be of any type — it does not have to be an array. This is one way in which &lt;code&gt;Array.reduce&lt;/code&gt; differs from other array methods like &lt;code&gt;map&lt;/code&gt; and &lt;code&gt;filter&lt;/code&gt;. Here's a reduce statement that returns the sum of an array of numbers:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const numbers = [1, 2, 3, 4, 5];
const sum = numbers.reduce((next, number) =&amp;gt; {
  return next + number;
}, 0);
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Array.reduce&lt;/code&gt; accepts two arguments:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;A callback function that is executed for each item in the array in series and receives the following parameters:

&lt;ul&gt;
&lt;li&gt;The &lt;em&gt;accumulator&lt;/em&gt; (called &lt;code&gt;next&lt;/code&gt; in the example), which is the working value. In the first iteration the accumulator is set to the initial value (&lt;code&gt;0&lt;/code&gt;). For all subsequent iterations it's the value returned by the previous iteration.&lt;/li&gt;
&lt;li&gt;The current array item (&lt;code&gt;number&lt;/code&gt; in the example).&lt;/li&gt;
&lt;li&gt;The current array index (not used in the example).&lt;/li&gt;
&lt;li&gt;The array that is being reduced (not used in the example).&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;The initial value for the accumulator. In &lt;em&gt;Example 1&lt;/em&gt;, the initial value is &lt;code&gt;0&lt;/code&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The reduce statement in &lt;code&gt;Example 1&lt;/code&gt; will execute the callback function five times with the following values:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator (&lt;code&gt;next&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;0&lt;/code&gt; (the initial value); &lt;strong&gt;Value (&lt;code&gt;number&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;1&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;2&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;3&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;3&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;3&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;6&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;6&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;4&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;10&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;10&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;5&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;15&lt;/code&gt;;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The final value of &lt;code&gt;sum&lt;/code&gt; will be &lt;code&gt;15&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Array.reduce applied to objects
&lt;/h2&gt;

&lt;p&gt;Remember that &lt;code&gt;Array.reduce&lt;/code&gt; can use initial and return values of any type, which makes it very flexible. Let's explore how we can use it to perform some common tasks with plain objects.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Converting an array of objects to a single object keyed by id:
&lt;/h3&gt;

&lt;p&gt;Developers frequently have to look up a value in one array using a value from another array. Consider the following hypothetical example where we have an array of objects representing users and another array representing profiles. Each user has an &lt;code&gt;id&lt;/code&gt; property and each profile has a &lt;code&gt;userId&lt;/code&gt; property. We need to match each user with their profile, where &lt;code&gt;user.id&lt;/code&gt; equals &lt;code&gt;profile.userId&lt;/code&gt;. A basic implementation of this is shown in &lt;em&gt;Example 2&lt;/em&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const users = [
  { id: 1, email: 'dcontreras@email.tld' },
  { id: 2, email: 'afeher@email.tld' },
  { id: 3, email: 'odj@email.tld' },
];

const profiles = [
  { userId: 1, firstName: 'Danielle', lastName: 'Contreras' },
  { userId: 2, firstName: 'Alfredas', lastName: 'Fehér' },
  { userId: 3, firstName: 'Orpheus', lastName: 'De Jong' },
];

const usersWithProfiles = users.map((user) =&amp;gt; {
  const profile = profiles.find((profile) =&amp;gt; (user.id === profile.userId));
  return { ...user, profile };
});

// usersWithProfiles:
// [
//   { id: 1, email: 'dcontreras@email.tld', profile: { userId: 1, firstName: 'Danielle', lastName: 'Contreras' } },
//   { id: 2, email: 'afeher@email.tld', profile: { userId: 2, firstName: 'Alfredas', lastName: 'Fehér' } },
//   { id: 3, email: 'odj@email.tld', profile: { userId: 3, firstName: 'Orpheus', lastName: 'De Jong' } },
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The problem with &lt;em&gt;Example 2&lt;/em&gt; is that it uses &lt;code&gt;Array.find&lt;/code&gt; inside &lt;code&gt;Array.map&lt;/code&gt; which is inefficient. This might not matter for the short arrays in the example, but it will become more of a problem when working with longer arrays. The longer the &lt;code&gt;profiles&lt;/code&gt; array is, the longer the potential lookup time will be for any given profile. We can solve this problem by transforming the &lt;code&gt;profiles&lt;/code&gt; array into an object beforehand, using the &lt;code&gt;userId&lt;/code&gt; property as the key:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 3:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const users = [
  { id: 1, email: 'dcontreras@email.tld' },
  { id: 2, email: 'afeher@email.tld' },
  { id: 3, email: 'odj@email.tld' },
];

const profiles = [
  { userId: 1, firstName: 'Danielle', lastName: 'Contreras' },
  { userId: 2, firstName: 'Alfredas', lastName: 'Fehér' },
  { userId: 3, firstName: 'Orpheus', lastName: 'De Jong' },
];

// Transform the profiles into an object keyed by the userId:
const profilesByUserId = profiles.reduce((next, profile) =&amp;gt; {
  const { userId } = profile;
  return { ...next, [userId]: profile };
}, {});

// profilesByUserId:
// {
//   1: { userId: 1, firstName: 'Danielle', lastName: 'Contreras' },
//   2: { userId: 2, firstName: 'Alfredas', lastName: 'Fehér' },
//   3: { userId: 3, firstName: 'Orpheus', lastName: 'De Jong' },
// }

// Look up the profiles by id:
const usersWithProfiles = users.map((user) =&amp;gt; {
  return { ...user, profile: profilesByUserId[user.id] };
});

// usersWithProfiles:
// [
//   { id: 1, email: 'dcontreras@email.tld', profile: { userId: 1, firstName: 'Danielle', lastName: 'Contreras' } },
//   { id: 2, email: 'afeher@email.tld', profile: { userId: 2, firstName: 'Alfredas', lastName: 'Fehér' } },
//   { id: 3, email: 'odj@email.tld', profile: { userId: 3, firstName: 'Orpheus', lastName: 'De Jong' } },
// ]
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example 3&lt;/em&gt; produces the same result as &lt;em&gt;Example 2&lt;/em&gt; but will be much faster with long arrays.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Copying an object with filtered properties:
&lt;/h3&gt;

&lt;p&gt;Sometimes you need a copy of an object that only includes certain properties from the original object, or that omits certain properties. This is a great use case for &lt;code&gt;Array.reduce&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 4:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Copy an object, retaining allowed properties:

const person = {
  firstName: 'Orpheus',
  lastName: 'De Jong',
  phone: '+1 123-456-7890',
  email: 'fake@email.tld',
};

const allowedProperties = ['firstName', 'lastName'];

const allKeys = Object.keys(person);
const result = allKeys.reduce((next, key) =&amp;gt; {
  if (allowedProperties.includes(key)) {
    return { ...next, [key]: person[key] };
  } else {
    return next;
  }
}, {});

// result:
// { firstName: 'Orpheus', lastName: 'De Jong' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example 4&lt;/em&gt; reduces the keys from the &lt;code&gt;person&lt;/code&gt; object into a new object that only contains the properties whose keys are included in the &lt;code&gt;allowedProperties&lt;/code&gt; array. If you add a new property to the &lt;code&gt;person&lt;/code&gt; object it will not appear in the result unless you also add the property name to the allowed properties.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 5:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Copy an object, excluding disallowed properties:

const person = {
  firstName: 'Orpheus',
  lastName: 'De Jong',
  phone: '+1 123-456-7890',
  email: 'odj@email.tld',
};

const disallowedProperties = ['phone', 'email'];

const allKeys = Object.keys(person);
const result = allKeys.reduce((next, key) =&amp;gt; {
  if (!disallowedProperties.includes(key)) {
    return { ...next, [key]: person[key] };
  } else {
    return next;
  }
}, {});

// result:
// { firstName: 'Orpheus', lastName: 'De Jong' }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example 5&lt;/em&gt; reduces the keys from the &lt;code&gt;person&lt;/code&gt; object into a new object that only contains the properties whose keys are &lt;em&gt;not&lt;/em&gt; included in the &lt;code&gt;disallowedProperties&lt;/code&gt; array. If you add a new property to the &lt;code&gt;person&lt;/code&gt; object it &lt;em&gt;will&lt;/em&gt; appear in the result unless you also add the property name to the disallowed properties. If you want to ensure that only certain properties will be included in the result, &lt;em&gt;Example 4&lt;/em&gt; is a better choice, but &lt;em&gt;Example 5&lt;/em&gt; is useful when you only need to ensure that certain properties will never included.&lt;/p&gt;

&lt;p&gt;We can also create generic functions for the reduce statements in examples 4 and 5:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 6:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const filterAllowedObjectProperties = (obj, allowedProperties = []) =&amp;gt; {
  return Object.keys(obj).reduce((next, key) =&amp;gt; {
    if (allowedProperties.includes(key)) {
      return { ...next, [key]: obj[key] };
    } else {
      return next;
    }
  }, {});
}

const filterDisallowedObjectProperties = (obj, disallowedProperties = []) =&amp;gt; {
  return Object.keys(obj).reduce((next, key) =&amp;gt; {
    if (!disallowedProperties.includes(key)) {
      return { ...next, [key]: obj[key] };
    } else {
      return next;
    }
  }, {});
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;
&lt;h3&gt;
  
  
  Merging two objects, preferring values from one:
&lt;/h3&gt;

&lt;p&gt;Another common task is to merge an object with another object that contains fallback, or default values for some properties. Sometimes you can do this simply by using object spread, but this can have unexpected consequences when you have null or empty properties:&lt;/p&gt;
&lt;h4&gt;
  
  
  Example 7:
&lt;/h4&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const obj1 = {
  key1: 'value 1.1',
  key2: null,
  key3: 'value 1.3',
  key4: ''
};

const obj2 = {
  key1: 'value 2.1',
  key2: 'value 2.2',
  key3: 'value 2.3',
  key4: 'value 2.4',
  key5: 'value 2.5'
};

const result = { ...obj2, ...obj1 };

// result:
//  {
//    key1: 'value 2.1',
//    key2: null,
//    key3: 'value 2.3',
//    key4: '',
//    key5: 'value 2.5'
//  };
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example 7&lt;/em&gt; creates a new object containing the properties from &lt;code&gt;obj2&lt;/code&gt; overridden by the properties from &lt;code&gt;obj1&lt;/code&gt;. Therefore, the values from &lt;code&gt;obj2&lt;/code&gt; serve as fallback or defaults for the values from &lt;code&gt;obj1&lt;/code&gt;. Notice that the result retains the &lt;code&gt;null&lt;/code&gt; and empty string values from &lt;code&gt;obj1&lt;/code&gt;. That happens because &lt;code&gt;null&lt;/code&gt; and an empty string are both &lt;em&gt;defined&lt;/em&gt; values. We probably didn't want this result but &lt;code&gt;Array.reduce&lt;/code&gt; offers a solution.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 8:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const obj1 = {
  key1: 'value 1.1',
  key2: null,
  key3: 'value 1.3',
  key4: ''
};

const obj2 = {
  key1: 'value 2.1',
  key2: 'value 2.2',
  key3: 'value 2.3',
  key4: 'value 2.4',
  key5: 'value 2.5'
};

// Spread the keys from both objects into an array.
const allKeys = [ ...Object.keys(obj1), ...Object.keys(obj2) ];

// Convert the array of keys to a set to remove duplicate values,
// then spread the unique values into a new array.
const uniqueKeys = [ ...new Set(allKeys) ];

// Reduce the unique keys into a new object containing the value
// for each key from obj1, falling back to the value from obj2 if
// obj1[key] is falsey.
const result = uniqueKeys.reduce((next, key) =&amp;gt; {
  const value = obj1[key] || obj2[key];
  return { ...next, [key]: value };
}, {});

// result:
// {
//   key1: 'value 1.1',
//   key2: 'value 2.2',
//   key3: 'value 1.3',
//   key4: 'value 2.4',
//   key5: 'value 2.5',
// }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that &lt;em&gt;Example 8&lt;/em&gt; uses a naive strategy to decide when to use the fallback value. The fallback value (&lt;code&gt;obj2[key]&lt;/code&gt;) is used if the preferred value (&lt;code&gt;obj1[key]&lt;/code&gt;) is &lt;em&gt;falsey&lt;/em&gt;. That means &lt;code&gt;undefined&lt;/code&gt;, &lt;code&gt;null&lt;/code&gt;, an empty string, &lt;code&gt;0&lt;/code&gt; or &lt;code&gt;false&lt;/code&gt;. This may not be appropriate for all cases, since any of these values might be acceptable in your application. Revise the fallback condition as necessary. For example, replacing &lt;code&gt;const value = obj1[key] || obj2[key];&lt;/code&gt; with &lt;code&gt;const value = (obj1[key] !== undefined &amp;amp;&amp;amp; obj1[key] !== null) ? obj1[key] : obj2[key];&lt;/code&gt; will ensure that the fallback value is only used when the preferred value is &lt;code&gt;undefined&lt;/code&gt; or &lt;code&gt;null&lt;/code&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Parsing search/query strings:
&lt;/h3&gt;

&lt;p&gt;Finally, let's look at another very common task that developers often use a third party library to accomplish: parsing search strings (sometimes called query strings). Modern web browsers provide &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams/URLSearchParams" rel="noopener noreferrer"&gt;URLSearchParams&lt;/a&gt; to make quick work of this, but maybe you aren't writing code for a browser, or you have to support Internet Explorer, or you just want to try doing it yourself because that's how we learn. Whatever your reason, &lt;code&gt;Array.reduce&lt;/code&gt; is your friend.&lt;/p&gt;

&lt;p&gt;First, we need a search string. You might get this directly from &lt;code&gt;window.location.search&lt;/code&gt; in a browser or by parsing an URL. If you use React and react-router you might use the &lt;code&gt;useLocation&lt;/code&gt; hook:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;`const { search = '' } = useLocation();`
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;However you get the search string, you'll need to prepare it first:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 9a:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Get a search string:
const search = '?key1=value%201&amp;amp;key2=value%202&amp;amp;key3=value%203';

// Remove the leading '?':
const query = search.replace(/^\?/, '');

// Split the string on the ampersand to create an array of key-value strings:
const pairs = query.split('&amp;amp;');

// pairs:
// [ 'key1=value%201', 'key2=value%202', 'key3=value%203' ];
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Next, reduce the key-value strings into an object by splitting them on the equals sign. The string before &lt;code&gt;=&lt;/code&gt; is the key and the remainder is the value. The value needs to be decoded with &lt;code&gt;decodeURIComponent&lt;/code&gt;.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 9b:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const params = pairs.reduce((next, pair) =&amp;gt; {
  const [ key, value ] = pair.split('=');
  const decodedValue = decodeURIComponent(value);
  return { ...next, [key]: decodedValue };
}, {});

// params:
// {
//   key1: 'value 1',
//   key2: 'value 2',
//   key3: 'value 3',
// }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The parser in &lt;em&gt;Example 9a/9b&lt;/em&gt; will do the job in many cases, but it's incomplete. Search strings can contain multiple values for each key, and this parser will only retain the last value for each key. Let's fix that.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 10:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const search = '?key1=value%201&amp;amp;key2=value%202&amp;amp;key3=value%203.1&amp;amp;key3=value%203.2&amp;amp;key3=value%203.3';
const query = search.replace(/^\?/, '');
const pairs = query.split('&amp;amp;');

const params = pairs.reduce((next, pair) =&amp;gt; {
  const [ key, value ] = pair.split('=');
  const decodedValue = decodeURIComponent(value);
  const previousValue = next[key];
  let nextValue;

  if (previousValue !== undefined) {
    if (Array.isArray(previousValue)) {
      nextValue = [ ...previousValue, decodedValue ];
    } else {
      nextValue = [ previousValue, decodedValue ];
    }
  } else {
    nextValue = decodedValue;
  }

  return { ...next, [key]: nextValue };
}, {});

// params:
// {
//   key1: 'value 1',
//   key2: 'value 2',
//   key3: [ 'value 3.1', 'value 3.2', 'value 3.3' ],
// }
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;em&gt;Example 10&lt;/em&gt; prepares the search string exactly the same way as &lt;em&gt;Example 9a&lt;/em&gt;. The difference is how the reduce callback handles the value for each key. Here's a step by step breakdown of the callback function:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The key-value string (&lt;code&gt;pair&lt;/code&gt;) is split on &lt;code&gt;=&lt;/code&gt; to get separate strings for the key and value.&lt;/li&gt;
&lt;li&gt;The value is decoded with &lt;code&gt;decodeURIComponent&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The accumulator (&lt;code&gt;next&lt;/code&gt;) is checked to determine if there is a previous value for the key.&lt;/li&gt;
&lt;li&gt;If there is a previous value (&lt;code&gt;previousValue !== undefined&lt;/code&gt;) it is checked to determine whether it's an array.&lt;/li&gt;
&lt;li&gt;If the previous value is an array, the decoded value is appended to it. (&lt;code&gt;nextValue = [ ...previousValue, decodedValue ];&lt;/code&gt;) If the previous value isn't an array, a new array is created containing the previous and decoded values. (&lt;code&gt;nextValue = [ previousValue, decodedValue ];&lt;/code&gt;)&lt;/li&gt;
&lt;li&gt;If there is no previous value, the next value is set to the decoded value. (&lt;code&gt;nextValue = decodedValue;&lt;/code&gt;)&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The resulting &lt;code&gt;params&lt;/code&gt; object contains string values for &lt;code&gt;key1&lt;/code&gt; and &lt;code&gt;key2&lt;/code&gt;, and an array containing the three strings for &lt;code&gt;key3&lt;/code&gt; in the order in which they appeared in the search string.&lt;/p&gt;

&lt;p&gt;Like we did in &lt;em&gt;Example 1&lt;/em&gt;, we can clarify the process by examining the state of each iteration:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator (&lt;code&gt;next&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;{}&lt;/code&gt; (the initial value); &lt;strong&gt;Value (&lt;code&gt;pair&lt;/code&gt;):&lt;/strong&gt; &lt;code&gt;'key1=value%201&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1' }&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1' }&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;'key2=value%202&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2' }&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2' }&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;'key3=value%203.1&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2', key3: 'value 3.1' }&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2', key3: 'value 3.1' }&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;'key3=value%203.2&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2', key3: ['value 3.1', 'value 3.2'] }&lt;/code&gt;;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Accumulator:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2', key3: ['value 3.1', 'value 3.2'] }&lt;/code&gt;; &lt;strong&gt;Value:&lt;/strong&gt; &lt;code&gt;'key3=value%203.3&lt;/code&gt;; &lt;strong&gt;Returns:&lt;/strong&gt; &lt;code&gt;{ key1: 'value 1', key2: 'value 2', key3: ['value 3.1', 'value 3.2', 'value 3.3'] }&lt;/code&gt;;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Summary:
&lt;/h2&gt;

&lt;p&gt;&lt;code&gt;Array.reduce&lt;/code&gt; is sort of a Swiss army knife that you can use to solve a wide variety of problems. I encourage you to explore it and try applying it in situations you might not have considered.&lt;/p&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>Are you using Array.map correctly?</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Tue, 04 Jan 2022 14:03:55 +0000</pubDate>
      <link>https://dev.to/redbit/are-you-using-arraymap-correctly-716</link>
      <guid>https://dev.to/redbit/are-you-using-arraymap-correctly-716</guid>
      <description>&lt;h2&gt;
  
  
  Array.map - Appropriate and Inappropriate Uses
&lt;/h2&gt;

&lt;p&gt;Lately, I've noticed a trend towards the inappropriate use of &lt;code&gt;Array.map&lt;/code&gt;, both in tutorials and production code. I'm not sure exactly why this is happening, but I think it may stem from the prevalence of &lt;code&gt;Array.map&lt;/code&gt; in React JSX components. JSX code typically uses &lt;code&gt;Array.map&lt;/code&gt; to render a component hierarchy for each item in an array.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React from 'react';
import PropTypes from 'prop-types';

const ItemsList = (props) =&amp;gt; {
  const { items = [] } = props;

  return (
    &amp;lt;ul className="items-list"&amp;gt;
      {items.map((item) =&amp;gt; (
        &amp;lt;li key={item.id}&amp;gt;{item.content}&amp;lt;/li&amp;gt;
      ))}
    &amp;lt;/ul&amp;gt;
  );
};

ItemsList.propTypes = {
  items: Proptypes.arrayOf(
    PropTypes.shape({
      id: PropTypes.string.isRequired,
      content: PropTypes.node.isRequired,
    })
  ).isRequired,
};

export default ItemsList;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The example above shows a component that renders an unordered list from an array of objects (named &lt;code&gt;items&lt;/code&gt;). Each item in the list is rendered with the &lt;code&gt;content&lt;/code&gt; property of the current object (&lt;code&gt;item&lt;/code&gt;) as the &lt;code&gt;items&lt;/code&gt; array is enumerated. If you've worked with React or similar rendering libraries before, this should be familiar. But a few details might not be completely obvious:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;items.map&lt;/code&gt; statement returns an array.&lt;/li&gt;
&lt;li&gt;The returned array is used, even though it isn't assigned to a variable or constant.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This can made more explicit by rewriting the component as follows:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const ItemsList = (props) =&amp;gt; {
  const { items = [] } = props;
  const listItems = items.map((item) =&amp;gt; (
    &amp;lt;li key={item.id}&amp;gt;{item.content}&amp;lt;/li&amp;gt;
  ));

  return (
    &amp;lt;ul className="items-list"&amp;gt;
      {listItems}
    &amp;lt;/ul&amp;gt;
  );
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;There is no behavioural difference between this and the previous version of &lt;code&gt;ItemsList&lt;/code&gt;. The only change is that the &lt;code&gt;items.map&lt;/code&gt; statement's return value is now assigned to &lt;code&gt;const listItems&lt;/code&gt; before rendering. Whether you use one approach over the other in practice is mostly a question of style. The point of this example is to make the render flow more explicit. In either case, the &lt;code&gt;items&lt;/code&gt; array is enumerated by the &lt;code&gt;items.map&lt;/code&gt; statement, which returns an array of JSX components to be rendered.&lt;/p&gt;

&lt;p&gt;The React/JSX example demonstrates the correct use of &lt;code&gt;Array.map&lt;/code&gt; — as a means of transforming the contents of an array. Here are some conventional examples:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 3:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Multiply each number in an array by 2:

const numbers = [1,2,3,4,5].map(n =&amp;gt; n * 2);
console.log(numbers);

// Result:
// [2,4,6,8,10]

// Get full names for an array of people:

const guitarists = [
  { firstName: 'Bill', lastName: 'Frisell' },
  { firstName: 'Vernon', lastName: 'Reid' },
];

const names = guitarists.map((guitarist) =&amp;gt; {
  const { firstName, lastName } = guitarist;
  return `${firstName} ${lastName}`;
});

console.log(names);

// Result:
// ['Bill Frisell', 'Vernon Reid']

// Add the full names to an array of people:

const guitaristsWithFullNames = guitarists.map((guitarist) =&amp;gt; {
  const { firstName, lastName } = guitarist;
  return { ...guitarist, fullName: `${firstName} ${lastName}` };
});

console.log(guitaristsWithFullNames);

// Result:
/*
[
  { firstName: 'Bill', lastName: 'Frisell', fullName: 'Bill Frisell' },
  { firstName: 'Vernon', lastName: 'Reid', fullName: 'Vernon Reid' },
]
*/
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Now that we've looked at some appropriate use cases for &lt;code&gt;Array.map&lt;/code&gt;, let's look at an inappropriate use case:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 4:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1,2,3,4,5].map((number) =&amp;gt; console.log(number));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is a trivial example that uses &lt;code&gt;Array.map&lt;/code&gt; to execute a side effect for each item in an array. (In this case, the side effect is a call to &lt;code&gt;console.log&lt;/code&gt; but that doesn't matter. You could substitute any other function.) This code works and is familiar to beginning JavaScript developers because it's used so frequently in JSX, so what's wrong with it?&lt;/p&gt;

&lt;p&gt;To put it simply, just because something &lt;em&gt;works&lt;/em&gt; doesn't always mean it's correct or appropriate. The use of &lt;code&gt;Array.map&lt;/code&gt; in &lt;em&gt;Example 4&lt;/em&gt; is inappropriate because it doesn't conform to the method's intended purpose. True to its name, &lt;code&gt;Array.map&lt;/code&gt; is intended to be used to &lt;em&gt;map&lt;/em&gt; (or transform) data from one structure to another. All the appropriate use cases we looked at follow this pattern:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;An array of data is mapped to an array of JSX components.&lt;/li&gt;
&lt;li&gt;An array of numbers is mapped to an array of the same numbers multiplied by 2.&lt;/li&gt;
&lt;li&gt;An array of objects representing people is converted to (or extended with) their full names.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Using &lt;code&gt;Array.map&lt;/code&gt; for anything other than mapping creates a few problems. First and foremost, it makes the code less clear. A developer reading code should expect &lt;code&gt;Array.map&lt;/code&gt; to perform some kind of transform and for the return value to be used. Second, the return value is always created whether you use it or not. In &lt;em&gt;Example 4&lt;/em&gt;, the &lt;code&gt;Array.map&lt;/code&gt; callback returns &lt;code&gt;undefined&lt;/code&gt;. That means the &lt;code&gt;Array.map&lt;/code&gt; statement returns an array containing an &lt;code&gt;undefined&lt;/code&gt; value for each index — &lt;code&gt;[1,2,3,4,5]&lt;/code&gt; maps to &lt;code&gt;[undefined, undefined, undefined, undefined, undefined]&lt;/code&gt;. Aside from being messy, there are performance costs associated with the unnecessary creation and disposal of those unused return values. Used in this way, &lt;code&gt;Array.map&lt;/code&gt; is slower than the appropriate alternative, &lt;code&gt;Array.forEach&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Array.forEach
&lt;/h2&gt;

&lt;p&gt;If we rewrite &lt;em&gt;Example 4&lt;/em&gt; using &lt;code&gt;Array.forEach&lt;/code&gt; instead of &lt;code&gt;Array.map&lt;/code&gt;, we eliminate these problems:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[1,2,3,4,5].forEach((number) =&amp;gt; console.log(number));
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;code&gt;Array.forEach&lt;/code&gt; is intended to be used this way and does not create an unnecessary return value for each item in the array. It's both clearer and faster.&lt;/p&gt;

&lt;h3&gt;
  
  
  Array.forEach vs. the for loop
&lt;/h3&gt;

&lt;p&gt;&lt;code&gt;Array.forEach&lt;/code&gt; is similar in concept to a traditional &lt;code&gt;for&lt;/code&gt; loop, but has a few practical advantages. The obvious advantages are clarity and brevity. I think we can all agree that &lt;code&gt;Array.forEach&lt;/code&gt; is easier to read and write:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 5:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// Array.forEach:

[1,2,3,4,5].forEach((number) =&amp;gt; console.log(number));

// for loop:

const numbers = [1,2,3,4,5];

for (let i = 0; i &amp;lt; numbers.length; i++) {
  console.log(numbers[i]);
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Another hidden advantage is that &lt;code&gt;Array.forEach&lt;/code&gt; handles arrays with uninitialized or deleted indexes (&lt;em&gt;sparse arrays&lt;/em&gt;) gracefully. Consider the following example, which enumerates an array whose third index is uninitialized:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 6:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const numbers = [1,2,,4,5];

// Array.forEach:

numbers.forEach((number, i) =&amp;gt; console.log(`number: ${number}, index: ${i}`));

// Result:
// number 1, index 0
// number 2, index 1
// number 4, index 3
// number 5, index 4

// for loop:

for (let i = 0; i &amp;lt; numbers.length; i++) {
  console.log(`number: ${numbers[i]}, index: ${i}`);
}

// Result:
// number 1, index 0
// number 2, index 1
// number undefined, index 2
// number 4, index 3
// number 5, index 4
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Notice that &lt;code&gt;Array.forEach&lt;/code&gt; skips the uninitialized index, thereby avoiding any problems that may be created by working on an uninitialized (or deleted) value. In most cases this is a nice safety feature to have. The &lt;code&gt;for&lt;/code&gt; loop is unforgiving and enumerates uninitialized values just like initialized values. If you want to ignore them, you have to do it manually. The side effect of having &lt;code&gt;Array.forEach&lt;/code&gt; perform those checks for you automatically is that it's somewhat slower than a &lt;code&gt;for&lt;/code&gt; loop written &lt;em&gt;without&lt;/em&gt; checks. &lt;strong&gt;&lt;em&gt;Before you go rewriting all your &lt;code&gt;Array.forEach&lt;/code&gt; code with &lt;code&gt;for&lt;/code&gt; loops to try to save a thousandth of a millisecond, keep in mind that the performance hit is negligible in the vast majority of real world use cases.&lt;/em&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Summary:
&lt;/h2&gt;

&lt;p&gt;Why should you choose &lt;code&gt;Array.map&lt;/code&gt;, &lt;code&gt;Array.forEach&lt;/code&gt; or a traditional &lt;code&gt;for&lt;/code&gt; loop?&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Choose &lt;code&gt;Array.map&lt;/code&gt; if you need to create a new array containing transformations of the items in the source array.&lt;/li&gt;
&lt;li&gt;Choose &lt;code&gt;Array.forEach&lt;/code&gt; when you simply need to enumerate the values in an array.&lt;/li&gt;
&lt;li&gt;Choose a &lt;code&gt;for&lt;/code&gt; loop only if absolute performance is critical (at the expense of stability and readability) or you still have to support Internet Explorer 8 (in which case you have my sympathy).&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>javascript</category>
    </item>
    <item>
      <title>React Function Components: Testable Code Patterns</title>
      <dc:creator>Tony Wallace</dc:creator>
      <pubDate>Tue, 04 Jan 2022 13:52:16 +0000</pubDate>
      <link>https://dev.to/redbit/react-function-components-testable-code-patterns-3c61</link>
      <guid>https://dev.to/redbit/react-function-components-testable-code-patterns-3c61</guid>
      <description>&lt;h2&gt;
  
  
  The Problem
&lt;/h2&gt;

&lt;p&gt;The advent of function components has introduced new ways to think about component design in React. We can write code that's cleaner and easier to understand, while dispensing with a lot of the boilerplate code required by class components. This should be a win for developers (and hopefully for future code maintainers) but the patterns that have been demonstrated in many tutorials and adopted by many developers leave something to be desired: testability. Consider the example shown in Example 1.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 1
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;import React, { useState } from 'react';
import PropTypes from 'prop-types';

const AddButton = (props) =&amp;gt; {
  const { initialNumber, addNumber } = props;
  const [ sum, setSum ] = useState(initialNumber);

  const addToSum = () =&amp;gt; {
    setSum(sum + addNumber);
  };

  return (
    &amp;lt;button onClick={addToSum}&amp;gt;
      Add {addNumber} to {sum}
    &amp;lt;/button&amp;gt;
  );
};

AddButton.defaultProps = {
  initialNumber: 0,
  addNumber: 1,
};

AddButton.propTypes = {
  initialNumber: PropTypes.number.isRequired,
  addNumber: PropTypes.number.isRequired,
};

export default AddButton;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is a trivial component that adds a number to a sum each time a button is pressed &amp;amp;emdash; the sort of thing you'll find in a typical tutorial. The component accepts an initial number and the number to add as props. The initial number is set as the initial sum on state and each press of the button updates the sum by adding the number to it. There isn't much to this component. The business logic consists of the &lt;code&gt;addToSum&lt;/code&gt; function, which amounts to a simple math expression whose result is passed to the &lt;code&gt;setSum&lt;/code&gt; state setter. It should be very easy to test that this produces the correct result, but it isn't because &lt;code&gt;addToSum&lt;/code&gt; is declared within the component's scope and can't be accessed from outside the component. Let's make a few small changes to fix that. Example 2 moves the logic into a separate function, so we can test that the math is correct.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 2
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// functions.js

export const add = (a, b) =&amp;gt; {
  return a + b;
};

// functions.test.js

import { add } from './functions';

test('The add function calculates the sum of two numbers', () =&amp;gt; {
  const sum = add(4, 5);
  expect(sum).toEqual(9);
});

// component.js

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { add } from './functions';

const AddButton = (props) =&amp;gt; {
  const { initialNumber, addNumber } = props;
  const [ sum, setSum ] = useState(initialNumber);

  const addToSum = () =&amp;gt; {
    setSum(add(sum, addNumber));
  };

  return (
    &amp;lt;button onClick={addToSum}&amp;gt;
      Add {addNumber} to {sum}
    &amp;lt;/button&amp;gt;
  );
};

AddButton.defaultProps = {
  initialNumber: 0,
  addNumber: 1,
};

AddButton.propTypes = {
  initialNumber: PropTypes.number.isRequired,
  addNumber: PropTypes.number.isRequired,
};

export default AddButton;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;This is &lt;em&gt;slightly&lt;/em&gt; better. We can test that the sum will be calculated correctly but we still have that pesky &lt;code&gt;addToSum&lt;/code&gt; function littering up our component and we still can't test that the sum is actually set on state. We can fix both of these problems by introducing a pattern that I call an &lt;em&gt;effect function&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Introducing Effect Functions
&lt;/h2&gt;

&lt;p&gt;An effect function is really just a closure &amp;amp;emdash; a function that returns another function &amp;amp;emdash; in which the inner function has access to the outer function's scope. This pattern is nothing new. It has been widely used as a solution to scope problems in JavaScript for a long time. We're just going to put it to use to improve the structure and testability of our React components. I call it an effect function because of how it integrates with React's &lt;code&gt;useEffect&lt;/code&gt; hook and other event handlers, which we'll see later on.&lt;/p&gt;

&lt;p&gt;Example 3 builds on Example 2 by moving all the logic into an effect function called &lt;code&gt;addToSumEffect&lt;/code&gt;. This cleans up the component nicely and allows us to write more comprehensive tests.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 3
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// functions.js

export const add = (a, b) =&amp;gt; {
  return a + b;
};

// functions.test.js

import { add } from './functions';

test('The add function calculates the sum of two numbers', () =&amp;gt; {
  const sum = add(4, 2);
  expect(sum).toEqual(6);
});

// effects.js

import { add } from './functions';

export const addToSumEffect = (options = {}) =&amp;gt; {
  const { addNumber, sum, setSum } = options;
  return () =&amp;gt; {
    setSum(add(sum, addNumber));
  };
};

// effects.test.js

import { addToSumEffect } from './effects';

test('addToSumEffect returns a function', () =&amp;gt; {
  const addNumber = 4;
  const sum = 2;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  expect(typeof func).toEqual('function');
});

test('The function returned by addToSumEffect calls setSum with the expected value', () =&amp;gt; {
  const addNumber = 4;
  const sum = 2;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  func();
  expect(setSum).toHaveBeenCalledTimes(1);
  expect(setSum).toHaveBeenCalledWith(6);
});

// component.js

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import { addToSumEffect } from './effects';

const AddButton = (props) =&amp;gt; {
  const { initialNumber, addNumber } = props;
  const [ sum, setSum ] = useState(initialNumber);

  return (
    &amp;lt;button onClick={addToSumEffect({ addNumber, sum, setSum })}&amp;gt;
      Add {addNumber} to {sum}
    &amp;lt;/button&amp;gt;
  );
};

AddButton.defaultProps = {
  initialNumber: 0,
  addNumber: 1,
};

AddButton.propTypes = {
  initialNumber: PropTypes.number.isRequired,
  addNumber: PropTypes.number.isRequired,
};

export default AddButton;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The code has changed a lot compared to Example 1, so let's walk through it beginning with the component. The component imports &lt;code&gt;addToSumEffect&lt;/code&gt; from a separate file and assigns its return value to the button's &lt;code&gt;onClick&lt;/code&gt; prop. &lt;code&gt;addToSumEffect&lt;/code&gt; is the closure's outer function. Its return value is the closure's inner function, which will be called when the button is pressed. &lt;code&gt;addToSumEffect&lt;/code&gt; accepts an &lt;code&gt;options&lt;/code&gt; hash containing the current values of &lt;code&gt;addNumber&lt;/code&gt; and &lt;code&gt;sum&lt;/code&gt;, as well as the &lt;code&gt;setSum&lt;/code&gt; function. These arguments are unpacked in the outer function's scope, which makes them available to the inner function.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;export const addToSumEffect = (options = {}) =&amp;gt; {
  // Unpack arguments from the options hash in the outer function:
  const { addNumber, sum, setSum } = options;
  return () =&amp;gt; {
    // The values are scoped into the inner function:
    setSum(add(sum, addNumber));
  };
};
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The outer function is called on every render with the current &lt;code&gt;addNumber&lt;/code&gt;, &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;setSum&lt;/code&gt; values, which generates a new inner function each time. This ensures that, whenever the button is pressed, it has access to the most up-to-date values from the component. This makes the inner function a sort of snapshot of the component values at the time the component was last rendered.&lt;/p&gt;

&lt;p&gt;We can break this process down step by step for the sake of clarity:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The component renders&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;addToSumEffect&lt;/code&gt; is called with a hash of the current &lt;code&gt;addNumber&lt;/code&gt;, &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;setSum&lt;/code&gt; values from the component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;addToSumEffect&lt;/code&gt; returns a new function with the current &lt;code&gt;addNumber&lt;/code&gt;, &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;setSum&lt;/code&gt; values in scope&lt;/li&gt;
&lt;li&gt;The returned function is assigned to the button's &lt;code&gt;onClick&lt;/code&gt; prop&lt;/li&gt;
&lt;li&gt;The user presses or clicks the button and the returned function is called&lt;/li&gt;
&lt;li&gt;The new sum is calculated from the current &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;addNumber&lt;/code&gt; values&lt;/li&gt;
&lt;li&gt;The new sum is passed to &lt;code&gt;setSum&lt;/code&gt; which updates the sum on the component's state&lt;/li&gt;
&lt;li&gt;The component renders and the process begins again with the new value of &lt;code&gt;sum&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The behaviour of &lt;code&gt;addToSumEffect&lt;/code&gt; should be stable and predictable for any given values of &lt;code&gt;sum&lt;/code&gt; and &lt;code&gt;addNumber&lt;/code&gt;. We can confirm this with tests.&lt;/p&gt;

&lt;h2&gt;
  
  
  Testing Effect Functions
&lt;/h2&gt;

&lt;p&gt;Example 3 defines the two tests for &lt;code&gt;addToSumEffect&lt;/code&gt;. The first test simply confirms that &lt;code&gt;addToSumEffect&lt;/code&gt; returns a function, which means that it conforms to the expected pattern.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('addToSumEffect returns a function', () =&amp;gt; {
  const addNumber = 4;
  const sum = 2;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  expect(typeof func).toEqual('function');
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The second test calls the returned function, supplying a &lt;code&gt;jest.fn()&lt;/code&gt; mock function for &lt;code&gt;setSum&lt;/code&gt;, which enables us to test that &lt;code&gt;setSum&lt;/code&gt; was called appropriately by the returned function. We expect &lt;code&gt;setSum&lt;/code&gt; to have been called only once, with the sum of the &lt;code&gt;addNumber&lt;/code&gt; and &lt;code&gt;sum&lt;/code&gt; values. If the returned function calls &lt;code&gt;setSum&lt;/code&gt; more than once (or not at all) or calls it with the incorrect value, the test will fail.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;test('The function returned by addToSumEffect calls setSum with the expected value', () =&amp;gt; {
  const addNumber = 2;
  const sum = 4;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  func();
  expect(setSum).toHaveBeenCalledTimes(1);
  expect(setSum).toHaveBeenCalledWith(6);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that we aren't testing the effect function's internal logic. We only care that &lt;code&gt;setSum&lt;/code&gt; is called once with the expected sum. We don't care how the effect function arrives at that result. The internal logic can change as long as the result remains the same.&lt;/p&gt;

&lt;h2&gt;
  
  
  Using Effect Functions with the &lt;code&gt;useEffect&lt;/code&gt; Hook
&lt;/h2&gt;

&lt;p&gt;There's one more small enhancement we can make to the component shown in Example 3. Currently, nothing happens if the &lt;code&gt;initialNumber&lt;/code&gt; prop changes after the initial mount. If &lt;code&gt;initialNumber&lt;/code&gt; changes, I'd like it to be set as the new value of &lt;code&gt;sum&lt;/code&gt; on state. We can do that easily by declaring a new effect function called &lt;code&gt;initializeSumEffect&lt;/code&gt; as shown in Example 4.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 4
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// functions.js

export const add = (a, b) =&amp;gt; {
  return a + b;
};

// functions.test.js

import { add } from './functions';

test('The add function calculates the sum of two numbers', () =&amp;gt; {
  const sum = add(4, 2);
  expect(sum).toEqual(6);
});

// effects.js

import { add } from './functions';

export const addToSumEffect = (options = {}) =&amp;gt; {
  const { addNumber, sum, setSum } = options;
  return () =&amp;gt; {
    setSum(add(sum, addNumber));
  };
};

// NEW:
export const initializeSumEffect = (options = {}) =&amp;gt; {
  const { initialNumber, setSum } = options;
  return () =&amp;gt; {
    setSum(initialNumber);
  };
};

// effects.test.js

import { initializeSumEffect, addToSumEffect } from './effects';

// NEW:
test('initializeSumEffect returns a function', () =&amp;gt; {
  const initialNumber = 4;
  const setSum = jest.fn();
  const func = initializeSumEffect({ initialNumber, setSum });
  expect(typeof func).toEqual('function');
});

// NEW:
test('The function returned by initializeSumEffect calls setSum with the value of initialNumber', () =&amp;gt; {
  const initialNumber = 4;
  const setSum = jest.fn();
  const func = initializeSumEffect({ initialNumber, setSum });
  func();
  expect(setSum).toHaveBeenCalledTimes(1);
  expect(setSum).toHaveBeenCalledWith(initialNumber);
});

test('addToSumEffect returns a function', () =&amp;gt; {
  const addNumber = 4;
  const sum = 2;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  expect(typeof func).toEqual('function');
});

test('The function returned by addToSumEffect calls setSum with the expected value', () =&amp;gt; {
  const addNumber = 4;
  const sum = 2;
  const setSum = jest.fn();
  const func = addToSumEffect({ addNumber, sum, setSum });
  func();
  expect(setSum).toHaveBeenCalledTimes(1);
  expect(setSum).toHaveBeenCalledWith(6);
});

// component.js

import React, { useState, useEffect } from 'react';
import PropTypes from 'prop-types';
import { initializeSumEffect, addToSumEffect } from './effects';

const AddButton = (props) =&amp;gt; {
  const { initialNumber, addNumber } = props;
  const [ sum, setSum ] = useState(initialNumber);

  // New:
  useEffect(initializeSumEffect({ initialNumber, setSum }), [initialNumber]);

  return (
    &amp;lt;button onClick={addToSumEffect({ addNumber, sum, setSum })}&amp;gt;
      Add {addNumber} to {sum}
    &amp;lt;/button&amp;gt;
  );
};

AddButton.defaultProps = {
  initialNumber: 0,
  addNumber: 1,
};

AddButton.propTypes = {
  initialNumber: PropTypes.number.isRequired,
  addNumber: PropTypes.number.isRequired,
};

export default AddButton;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Let's break the new additions down step by step:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The component updates with a new value for the &lt;code&gt;initialNumber&lt;/code&gt; prop&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initializeSumEffect&lt;/code&gt; is called with a hash of the current &lt;code&gt;initialNumber&lt;/code&gt; and &lt;code&gt;setSum&lt;/code&gt; values from the component&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;initializeSumEffect&lt;/code&gt; returns a new function with the current &lt;code&gt;initialNumber&lt;/code&gt; and &lt;code&gt;setSum&lt;/code&gt; values in scope&lt;/li&gt;
&lt;li&gt;The returned function is assigned to the &lt;code&gt;useEffect&lt;/code&gt; hook (note that the hook is configured to run only when &lt;code&gt;initialNumber&lt;/code&gt; has changed, not on every render)&lt;/li&gt;
&lt;li&gt;The component renders&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;useEffect&lt;/code&gt; runs, calling the returned function&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;initialNumber&lt;/code&gt; value is passed to &lt;code&gt;setSum&lt;/code&gt; which updates the sum on the component's state&lt;/li&gt;
&lt;li&gt;The component renders&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also have new tests to confirm that &lt;code&gt;initializeSumEffect&lt;/code&gt; returns a function, and that the returned function calls &lt;code&gt;setSum&lt;/code&gt; with the expected value.&lt;/p&gt;

&lt;p&gt;Notice how similar &lt;code&gt;initializeSumEffect&lt;/code&gt; is to &lt;code&gt;addToSumEffect&lt;/code&gt; despite being used in different contexts. This is one of the benefits of this pattern. It works equally well whether you're working with React hooks, JavaScript event handlers, or both.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Less Trivial Example: API Integration
&lt;/h2&gt;

&lt;p&gt;The examples above are simple, which made them a good introduction to the effect function pattern. Let's look at how to apply this pattern to more of a real world integration: an asychronous API request that updates component state upon completion.&lt;/p&gt;

&lt;p&gt;The basic pattern for this is the same as the previous example. We'll use an effect function to perform the request when the component mounts, then set the response body (or error) on the component state. Everything the effect consumes will be passed in from the component, so the effect function won't have external dependencies that would make it harder to test.&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 5
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// effects.js

export const getDataEffect = (options = {}) =&amp;gt; {
  const { url, getJson, setData, setError, setIsLoading } = options;
  return async () =&amp;gt; {
    setIsLoading(true);
    try {
      const data = await getJson(url);
      setData(data);
      setError(null);
      setIsLoading(false);
    } catch (error) {
      setError(error);
      setIsLoading(false);
    }
  };
};

// component.js

import React, { useState, useEffect } from 'react';
import { getDataEffect } from './effects';
import { getJson } from './requests';
import { LoadingIndicator } from './loading';
import { DataView } from './data-view';

const DataPage = (props) =&amp;gt; {
  const [ data, setData ] = useState({});
  const [ error, setError ] = useState(null);
  const [ isLoading, setIsLoading ] = useState({});

  useEffect(
    getDataEffect({
      url: 'https://api.myapp.com/data',
      getJson,
      setData,
      setError,
      setIsLoading
    }),
    []
  );

  return (
    &amp;lt;div className="data-page"&amp;gt;
      {isLoading &amp;amp;&amp;amp; &amp;lt;LoadingIndicator /&amp;gt;}
      {error &amp;amp;&amp;amp; (
        &amp;lt;p className="error-message"&amp;gt;
          {error.message}
        &amp;lt;/p&amp;gt;
      )}
      {!error &amp;amp;&amp;amp; (&amp;lt;DataView data={data} /&amp;gt;)}
    &amp;lt;/div&amp;gt;
  );
};

export default DataPage;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Note that some elements in Example 5 are not described in detail because they don't fall within the scope of this discussion. &lt;code&gt;getJson&lt;/code&gt; is an async function that makes an  &lt;code&gt;GET&lt;/code&gt; request for some data and returns the data or throws an error. &lt;code&gt;LoadingIndicator&lt;/code&gt; is a component that displays loading activity or progress UI. &lt;code&gt;DataView&lt;/code&gt; is a component that displays the requested data. I have omitted these from the example so we can focus on the pattern. Let's break down the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The component mounts.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;getDataEffect&lt;/code&gt; is called with the request url, request function (&lt;code&gt;getJson&lt;/code&gt;) and setters for the &lt;code&gt;data&lt;/code&gt;, &lt;code&gt;error&lt;/code&gt; and &lt;code&gt;isLoading&lt;/code&gt; state values. &lt;code&gt;getDataEffect&lt;/code&gt; returns an async function.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;useEffect&lt;/code&gt; hook calls the async function that was returned by &lt;code&gt;getDataEffect&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;The async function sets the loading state to &lt;code&gt;true&lt;/code&gt;, which causes the loading indicator to render.&lt;/li&gt;
&lt;li&gt;The async function calls &lt;code&gt;getJson&lt;/code&gt; with the request url and waits for a response.&lt;/li&gt;
&lt;li&gt;Upon receiving a successful response, the async function sets the data on state, the error state to &lt;code&gt;null&lt;/code&gt; and the loading state to &lt;code&gt;false&lt;/code&gt;. The component stops rendering the loading indicator and passes the data to &lt;code&gt;DataView&lt;/code&gt; to be rendered.&lt;/li&gt;
&lt;li&gt;If &lt;code&gt;getJson&lt;/code&gt; throws an error, the async function sets the error on state and the loading state to &lt;code&gt;false&lt;/code&gt;. The component stops rendering the loading indicator and renders an error message.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Next, let's add tests for &lt;code&gt;getDataEffect&lt;/code&gt;:&lt;/p&gt;

&lt;h4&gt;
  
  
  Example 6:
&lt;/h4&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;// effects.test.js

import { getDataEffect } from './effects';

test('getDataEffect returns a function', () =&amp;gt; {
  const url = 'https://fake.url';
  const getJson = jest.fn();
  const setData = jest.fn();
  const setError = jest.fn();
  const setIsLoading = jest.fn();
  const func = getDataEffect({ url, getJson, setData, setError, setIsLoading });
  expect(typeof func).toEqual('function');
});

test('The function returned by getDataEffect behaves as expected when making a successful request', async () =&amp;gt; {
  const url = 'https://fake.url';
  const data = { status: true };

  // Mock the async getJson function to resolve with the data:
  const getJson = jest.fn();
  getJson.mockReturnValue(Promise.resolve(data));

  // Mock the setter functions:
  const setData = jest.fn();
  const setError = jest.fn();
  const setIsLoading = jest.fn();

  // Run the effect:
  const func = getDataEffect({ url, getJson, setData, setError, setIsLoading });
  await func();

  // Test that getJson was called once with the provided url:
  expect(getJson).toHaveBeenCalledTimes(1);
  expect(getJson).toHaveBeenCalledWith(url);

  // Test that setData was called once with the expected data:
  expect(setData).toHaveBeenCalledTimes(1);
  expect(setData).toHaveBeenCalledWith(data);

  // Test that setError was called once with null:
  expect(setError).toHaveBeenCalledTimes(1);
  expect(setError).toHaveBeenCalledWith(null);

  // Test that setIsLoading was called twice, with
  // true the first time and false the second time:
  expect(setIsLoading).toHaveBeenCalledTimes(2);
  expect(setIsLoading.mock.calls[0][0]).toBe(true);
  expect(setIsLoading.mock.calls[1][0]).toBe(false);
});

test('The function returned by getDataEffect behaves as expected when making an unsuccessful request', async () =&amp;gt; {
  const url = 'https://fake.url';
  const error = new Error(message);

  // Mock the async getJson function to reject with the error:
  const getJson = jest.fn();
  getJson.mockReturnValue(Promise.reject(error));

  // Mock the setter functions:
  const setData = jest.fn();
  const setError = jest.fn();
  const setIsLoading = jest.fn();

  // Run the effect:
  const func = getDataEffect({ url, getJson, setData, setError, setIsLoading });
  await func();

  // Test that getJson was called once with the provided url:
  expect(getJson).toHaveBeenCalledTimes(1);
  expect(getJson).toHaveBeenCalledWith(url);

  // Test that setData was not called:
  expect(setData).not.toHaveBeenCalled();

  // Test that setError was called once with the error:
  expect(setError).toHaveBeenCalledTimes(1);
  expect(setError).toHaveBeenCalledWith(error);

  // Test that setIsLoading was called twice, with
  // true the first time and false the second time:
  expect(setIsLoading).toHaveBeenCalledTimes(2);
  expect(setIsLoading.mock.calls[0][0]).toBe(true);
  expect(setIsLoading.mock.calls[1][0]).toBe(false);
});
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The first test just validates that &lt;code&gt;getDataEffect&lt;/code&gt; returns a function. It's the same basic sanity check we've used in all the other examples. The second test validates the entire flow for a successful request:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;We define a fake request run and data.&lt;/li&gt;
&lt;li&gt;We create a mock function for &lt;code&gt;getJson&lt;/code&gt; that returns a promise, which will resolve with the expected data.&lt;/li&gt;
&lt;li&gt;We create simple mock functions for the state setters.&lt;/li&gt;
&lt;li&gt;We call &lt;code&gt;getDataEffect&lt;/code&gt; to obtain the async function.&lt;/li&gt;
&lt;li&gt;We call the function and wait for it to return.&lt;/li&gt;
&lt;li&gt;We test that &lt;code&gt;getJson&lt;/code&gt; was called once with the provided url.&lt;/li&gt;
&lt;li&gt;We test that &lt;code&gt;setData&lt;/code&gt; was called once with the expected data.&lt;/li&gt;
&lt;li&gt;We test that &lt;code&gt;setError&lt;/code&gt; was called once with &lt;code&gt;null&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;We test that &lt;code&gt;setIsLoading&lt;/code&gt; was called twice, with &lt;code&gt;true&lt;/code&gt; the first time and &lt;code&gt;false&lt;/code&gt; the second time.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The third test validates the entire flow for an unsuccessful (error) request. It's similar to the second test but the expectations are different. The mock &lt;code&gt;getJson&lt;/code&gt; function returns a promise, which will reject with an error. &lt;code&gt;setError&lt;/code&gt; should be called with that error. &lt;code&gt;setData&lt;/code&gt; should not be called.&lt;/p&gt;

&lt;h2&gt;
  
  
  Wrapping Up
&lt;/h2&gt;

&lt;p&gt;We now have a consistent structure that keeps business logic out of our components and makes our code easier to read. We're also able to write comprehensive tests to validate that our code does the right thing, which can improve confidence in the codebase. (This assumes that you actually run your tests regularly and integrate them into your continuous integration pipeline, but that's a topic for another post.) This is one of many ways to structure your components. I hope it gives you some ideas to establish an architecture that suits your needs.&lt;/p&gt;

</description>
      <category>javascript</category>
      <category>react</category>
      <category>webdev</category>
      <category>testing</category>
    </item>
  </channel>
</rss>
