<?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: Abbey Perini</title>
    <description>The latest articles on DEV Community by Abbey Perini (@abbeyperini).</description>
    <link>https://dev.to/abbeyperini</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%2F564890%2F496b4464-61d3-4536-aea8-4637f368890d.jpg</url>
      <title>DEV Community: Abbey Perini</title>
      <link>https://dev.to/abbeyperini</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abbeyperini"/>
    <language>en</language>
    <item>
      <title>Surviving Cancer Doesn't Suck</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Tue, 06 Jan 2026 19:45:01 +0000</pubDate>
      <link>https://dev.to/abbeyperini/surviving-cancer-doesnt-suck-1ef5</link>
      <guid>https://dev.to/abbeyperini/surviving-cancer-doesnt-suck-1ef5</guid>
      <description>&lt;p&gt;My post-treatment recovery has been a period of joy, connection, and rest.&lt;/p&gt;

&lt;p&gt;It's been ten months since I finished radiation. I meant it when I said &lt;a href="https://dev.to/abbeyperini/cancer-treatment-sucks-3ccp#:~:text=I%20leapt%20back%20into%20life"&gt;I leapt back into life&lt;/a&gt;. Since treatment, I've given seven conference talks, including &lt;a href="https://www.youtube.com/watch?v=mt2v2DX5Tno" rel="noopener noreferrer"&gt;my first international one&lt;/a&gt;. I showed up to film an episode of the &lt;a href="https://codetv.dev/series/web-dev-challenge/s2/e1-build-a-custom-api-postman" rel="noopener noreferrer"&gt;Web Dev Challenge&lt;/a&gt; with almost no eyebrows and even fewer eyelashes than I left Atlanta with.&lt;/p&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%2F5f8ju9kd9ubuhnc7ki6o.jpg" 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%2F5f8ju9kd9ubuhnc7ki6o.jpg" alt="A group photo of season 2 episode 1 of the Web Dev Challenge with Abbey, Alex, Brittany, Dave, Will, Michael, Jason, and Stirling" width="800" height="564"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Most importantly, there have been days when I didn't even think about cancer.&lt;/p&gt;

&lt;p&gt;After radiation ended, people began asking about remission. Turns out, it depends on which doctor you're talking to. Since I could have been cured by surgery, my remission either started after surgery in July 2024 or after chemo in December 2024. Since there was no proof that my cancer had spread beyond the tumor, the rest of my treatment after surgery served to kill anything that &lt;em&gt;might have&lt;/em&gt; escaped. Cancer treatment is often frustratingly grey instead of black and white.&lt;/p&gt;

&lt;p&gt;By the end of March, my hair was still the first thing people mentioned every time they saw me. I had kept about 40% of my hair, but my scalp was still tender and healing. As a result, I couldn't use a hair dryer long enough to get it fully dry. I was still out-shedding my Shiba Inu. Tumbleweeds of long blonde hair followed me everywhere. They stubbornly stuck to my clothes through the washer and dryer. It felt like I was suffocating and that I should be grateful to have any hair simultaneously.&lt;/p&gt;

&lt;p&gt;My hair has never been straight, but the new growth I started to see was extremely curly. They call it the Chemo Curl. The new curls and shedding hairs formed mats. Dealing with the mats was painful and often resulted in more shedding. I tried keeping my hair braided, but the same tension every day pulled out more hair at the top of the braid. I began to feel like a fraud when people complimented my hair.&lt;/p&gt;

&lt;p&gt;Once I cut it, I feel free. More like me. A few months later, the inch-long ringlets made it impossible to style. I cringed when I saw it in photos. Now, I'm starting to get the hang of it. My hairstylist and a friend helped me navigate curly styling products. Some of my new ringlets are now three to four inches long. I wait to see if my chemo curl is temporary or permanent. Only time will tell.&lt;/p&gt;

&lt;p&gt;By April, the rest of the hair on my body made me feel like I was experiencing &lt;a href="https://www.youtube.com/watch?v=CoBKVzVgjys" rel="noopener noreferrer"&gt;squirrel pattern baldness&lt;/a&gt;.&lt;/p&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%2Ftlnqfdziiois173hhayd.jpg" 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%2Ftlnqfdziiois173hhayd.jpg" alt="Sandy the squirrel from Spongebob Squarepants missing all her fur" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Every time I looked in the mirror, I noticed new body hair. My brain didn't know what to do with the peach fuzz on my face growing straight out. I was covered in patchy and itchy stubble. Some of it temporarily and dramatically changed color or texture. As I approached normal, I took comfort in being hairy.&lt;/p&gt;

&lt;p&gt;During radiation, I could tell my scar tissue was changing. As I healed, I noticed changes in the surrounding healthy skin and muscle, as well. My range of motion decreased. I learned that the combination of surgery and radiation on my lymphatic channel (the tubes that hold your lymph nodes) was causing it to shrink. The shrinking pinched blood vessels and nerves, so I started getting nerve pain and circulation issues. I started physical therapy (PT) at a rehabilitation center.&lt;/p&gt;

&lt;p&gt;For a long time, my weekly PT appointments and daily self-administered PT were just fighting the radiation still working its way through my system. I was manually breaking up scar tissue as fast as it formed. Four months after radiation ended, I started seeing progress. My range of motion returned to normal. Everything started softening back up.&lt;/p&gt;

&lt;p&gt;And while all this was going on, I was living my best life. I'll never forget my week in Paris. I was still affected by feelings about the changes in my body, but I was so glad to be free from daily hospital rooms.&lt;/p&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%2Fcfg4o09m4ov6s7v3ua8z.jpg" 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%2Fcfg4o09m4ov6s7v3ua8z.jpg" alt="Abbey in a 1960's French Army rain coat in a park full of statues and tulips" width="800" height="1062"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;At our first dinner in Paris, I befriended a fellow tourist and cancer survivor who was finishing up the same drug I had just started. Her group whisked us away to the Eiffel Tower for its last sparkle of the night. My favorite song came on the radio on the way back. It felt like a movie.&lt;/p&gt;

&lt;p&gt;Most of all, I was relieved to see my husband relax. He had worked himself into knots making sure he was supporting me to the best of his ability. But as my need for support returned to close to normal, his stress level didn't. The joy, relaxation, delicious food, and forty-four miles of walking put my husband and I on a better path. It was also very special that he got to watch me speak at the historic Folies Bergère about a topic that affects both of us.&lt;/p&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%2Fed3t4q8df2990vz4vmef.jpg" 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%2Fed3t4q8df2990vz4vmef.jpg" alt="Abbey laughing and Abbey's husband talking at DotJS" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Back in the States, the rehabilitation center was the first cancer center in which no one  acted like it was weird that I was so young. After a handful of individual counseling sessions, I agreed to join the Young Survivors support group. For the first time, I had found people that were going through the same things. I finally wasn't alone.&lt;/p&gt;

&lt;p&gt;We complained about the people who hadn't faced treatment with us and now expected us to satisfy their curiosity by re-living our medical trauma. We lamented that they wouldn't ask the questions they actually want to ask. (They just keep saying "How are you? No really, &lt;em&gt;how are you?&lt;/em&gt;") We commiserated about the uncertainties, especially around having kids. I learned why so many of my doctors had reacted so dramatically to the name of the drug I'll be on for five years. The possible side effects can greatly affect your quality of life. Time and time again I have learned that it all could have been much worse.&lt;/p&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%2Fm546vb8d1irjt2hnmynx.jpg" 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%2Fm546vb8d1irjt2hnmynx.jpg" alt="When cancer patients are sharing and venting and someone tells them " width="710" height="595"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;No one will ever be able to tell me what caused my cancer. No one will ever be able to tell me if it will come back. In September 2024, this knowledge triggered an anxiety spiral in me, so I made the choice to not dwell on that. I had to believe treatment would work. I'll panic if there's something concrete to panic about. The more I interact with cancer survivors and their loved ones, the more I realize that reaction isn't the most common one.&lt;/p&gt;

&lt;p&gt;Doctors often ask me to complete questionnaires designed to catch anxiety and depression. I remember when it was challenging to pick an answer. No anxiety or depression would have been abnormal. Now, filling them out is a breeze.&lt;/p&gt;

&lt;p&gt;Every once in a while, I catch notes of jealousy and pain in the voice of a fellow cancer survivor who is trying to be happy for me while still struggling with the weight of it all. To be fair, I think I'm pretty good at hiding the jealousy I feel when I talk to people who are even luckier than me - the people who get to avoid chemo and radiation. I am amazed at the resilience of the survivors who supported me emotionally through treatment. Thankfully, we've never lost sight of the fact that we're all in a really cool, elite club that none of us wanted to be in.&lt;/p&gt;

&lt;p&gt;On the anniversary of my diagnosis, I was in the same building and attending the same conference where I got the call. I felt raw and vulnerable. I was surrounded by a big part of my support network. Instead of a very sad, difficult time, it felt like the final step of learning to trust my village.&lt;/p&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%2Ffpnidf3shyie11799fj6.jpg" 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%2Ffpnidf3shyie11799fj6.jpg" alt="Abbey, Kayla, and Richard dressed as Jessie, Buzz Lightyear, and Woody at RenderATL" width="800" height="737"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My nerves about my first scan post-treatment were about the logistics rather than the result. In the waiting room, I had a visceral reaction when they pulled out a wristband for me. Because I had never had a normal result from one of these scans, I had to ask the hospital staff to repeat the result. It felt surreal to get to leave without any fuss.&lt;/p&gt;

&lt;p&gt;I know I'm out of the woods because the system has begun to move slowly again. (You know it's bad when the U.S. healthcare system starts moving quickly.) For the foreseeable future, I'll be getting a scan every six months and an oncology appointment every three. It's common to have anxiety about the scans and lengthening the time between appointments. This still feels like a lot of appointments to me. I'm happy to spend less time in wristbands.&lt;/p&gt;

&lt;p&gt;Most of the feelings I still wrestle with are related to being a patient. I get nauseous if I think about chemo for too long. I used to laugh at how many supplements my parents took. Now we compare our daily regimens and studies we've read. I've had a needle phobia since I was six, and now I'm so experienced with needles that I give tips on how to find my veins.&lt;/p&gt;

&lt;p&gt;I try not to think about how many hours I've spent in medical waiting rooms. I debate making a medical resume to make doctors' forms easier. Often, other patients ask me about the knitting or crocheting project I'm working on to pass the time. I don't tell them that it's to prevent me from fleeing the waiting room. I don't mention that I'm glad to be done with the project I took to chemo and radiation.&lt;/p&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%2Frjlwca5504xjn7xv6v7q.jpg" 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%2Frjlwca5504xjn7xv6v7q.jpg" alt="A teal knit circular shawl with intricate cabling" width="800" height="1062"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As I run into people who haven't kept up with me, they often unwittingly remark on things that touch a nerve. Usually, about how my attitude or body has changed. I lament that most types of cancer treatment cause weight loss and I got one of the kinds that causes a lot of weight gain. I love that I put down many of the worries and obligations that no longer served me. I hate that I was forced to miss out on 5 months of regular life.&lt;/p&gt;

&lt;p&gt;Doctors tell me I'm stoic, a trooper, and a good patient. People tell me how strong I am. I don't know how to describe the river of tears I'm finally leaving behind. I can't put into words what it's like to learn that time can slow down until you're not just living day to day, but minute by minute. I deflect, calling it "stubborn persistence," and friends remind me that that's a kind of strength. I know that well is there deep within me and hope I don't need to draw from it again.&lt;/p&gt;

&lt;p&gt;I've realized that being able to understand everything I went through - what treatment does and why I needed to do it - is a large part of what kept me sane. There were only a few times where it was not enough - my body didn't want to do it anymore. Until you've had to willingly poison yourself, you don't know what tricks you'll have to play on your own brain to keep poisoning yourself. Still, for a long time, it felt like I had no agency - like cancer was the thing deciding my course.&lt;/p&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%2Fzz4wmafcty3wnzfvdjko.jpg" 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%2Fzz4wmafcty3wnzfvdjko.jpg" alt="When cancer muggles chase you with their unsolicited advice on how to deal with your diagnosis. A car labelled me followed by 11+ police cars on the highway. 7 of the cars are labelled with common unsolicited advice - just be positive and pray hard, but you don't look sick, chemo is poison, sugar feeds cancer cells, alkaline diet, pyramid schemes, essential oils" width="800" height="739"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Now, I'm almost done recovering from what was hopefully my final surgery. This time, it was my choice. I was surprised by how much that sense of agency mattered to me. It was cosmetic and I'm very happy with the results. It wasn't magic - I'll always have scars. The surgeon reported that my radiation scar tissue was way better than average, so all that PT was worth it. Which was a good thing to hear before I had to start a new round of post-surgery PT.&lt;/p&gt;

&lt;p&gt;This time, I practiced spite-driven rest. (Not a single person believed I would rest, so I had to prove them wrong.) Between that rest and the two weeks I took off for the holidays, I had a lot of time to see family and friends, reflect, and set goals for the coming year.&lt;/p&gt;

&lt;p&gt;It finally feels like the fresh start a doctor promised me a year and a half ago.&lt;/p&gt;

&lt;p&gt;Someone recently asked me "What's one thing you're doing today that you weren't doing a lot of a year ago?" and then we laughed and laughed. A year ago I could barely get out of bed. A year ago, I still had a long slog ahead of me. A year ago, I wondered how other people made it through longer treatment regimens. A year ago, I wondered if I would be able to do it without breaking.&lt;/p&gt;

&lt;p&gt;I'm glad I learned to bend more easily.&lt;/p&gt;

&lt;p&gt;Decisions that would have been difficult for me two years ago are now easy. I cry and feel love more freely. I find joy in even littler things. I trust myself and others. I try to avoid plot lines and ads about cancer patients. I collect stories from fellow survivors about &lt;a href="https://xkcd.com/3172/" rel="noopener noreferrer"&gt;being in remission for years and years.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I'm taking it a day at a time because I want to, not because I have to.&lt;/p&gt;

</description>
      <category>devjournal</category>
      <category>mentalhealth</category>
      <category>motivation</category>
      <category>watercooler</category>
    </item>
    <item>
      <title>Gang of Four Design Patterns in Memes - Creational</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Thu, 14 Aug 2025 23:22:42 +0000</pubDate>
      <link>https://dev.to/abbeyperini/gang-of-four-design-patterns-in-memes-creational-5gci</link>
      <guid>https://dev.to/abbeyperini/gang-of-four-design-patterns-in-memes-creational-5gci</guid>
      <description>&lt;p&gt;The Gang of Four (GoF) are Erich Gamma, Richard Helm, Ralph Johnson, and John Vlissides. They wrote &lt;em&gt;Design Patterns: Elements of Reusable Object-Oriented Software&lt;/em&gt;. In it, they documented 23 classic software design patterns.&lt;/p&gt;

&lt;p&gt;There are five creational design patterns - they cover the instantiation of objects. &lt;/p&gt;

&lt;h2&gt;
  
  
  Singleton
&lt;/h2&gt;

&lt;p&gt;There can only be one instance.&lt;/p&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%2Fkian7y60t4mep1rwad2i.jpg" 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%2Fkian7y60t4mep1rwad2i.jpg" alt="There can only be one. A Scottish warrior from the movie Highlander" width="750" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful for when more than one copy of an object or class would break your system.&lt;/p&gt;

&lt;h2&gt;
  
  
  Factory Method
&lt;/h2&gt;

&lt;p&gt;A factory is an interface for creating an object. Its subclasses determine  the shape of the instantiated object.&lt;/p&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%2Fg3cqoky6rx3trj5hdkx6.jpg" 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%2Fg3cqoky6rx3trj5hdkx6.jpg" alt="A factory worker sees three triangles and one circle on a conveyor belt. They say " width="800" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful for when the creation of some objects is complex enough to be abstracted and those objects have overlapping implementation details.&lt;/p&gt;

&lt;h2&gt;
  
  
  Abstract Factory
&lt;/h2&gt;

&lt;p&gt;Essentially a factory of factories - a list of creation methods for a group of factories.&lt;/p&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%2F1iucmp081vcf9i50cuch.jpg" 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%2F1iucmp081vcf9i50cuch.jpg" alt="Every friend group: My Hero Academia characters labeled " width="729" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful for factories need to be grouped together.&lt;/p&gt;

&lt;h2&gt;
  
  
  Builder
&lt;/h2&gt;

&lt;p&gt;A list of methods for constructing parts of an object.&lt;/p&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%2F8co0a1fywrh307evmldg.jpg" 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%2F8co0a1fywrh307evmldg.jpg" alt="Homer Simpson at the nuclear power plant operation board looking panicked labelled " width="553" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Useful for when you have so many optional parameters that passing them through a factory is error-prone.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prototype
&lt;/h2&gt;

&lt;p&gt;New objects are created by copying an existing object, inheriting characteristics from the parent object.  &lt;/p&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%2F3mfhecr4p9bvhjxrxcny.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%2F3mfhecr4p9bvhjxrxcny.png" alt="One person holding an object says " width="800" height="319"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;More efficient for complex object creation. JavaScript uses prototypical inheritance.&lt;/p&gt;

</description>
      <category>programming</category>
      <category>webdev</category>
      <category>oop</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>State Management in Front-end Web Development: Actions, Dispatch, and Reducers</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Sat, 12 Jul 2025 19:38:28 +0000</pubDate>
      <link>https://dev.to/abbeyperini/state-management-in-front-end-web-development-actions-dispatch-and-reducers-gfo</link>
      <guid>https://dev.to/abbeyperini/state-management-in-front-end-web-development-actions-dispatch-and-reducers-gfo</guid>
      <description>&lt;p&gt;In &lt;a href="https://dev.to/abbeyperini/state-management-in-front-end-web-development-state-101-48g3"&gt;State Management in Front-end Web Development: State 101&lt;/a&gt;, I covered when you need which state management tools and how they're implementations of the &lt;a href="https://en.wikipedia.org/wiki/Observer_pattern" rel="noopener noreferrer"&gt;observer pattern&lt;/a&gt;. This part of the series assumes that you need a state tool that uses the actions, dispatch, and reducers pattern. This pattern is useful when you need to consume and update the same state in multiple unrelated components. React &lt;a href="https://react.dev/learn/scaling-up-with-reducer-and-context" rel="noopener noreferrer"&gt;Context&lt;/a&gt;, and global state management libraries like &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt; for React and &lt;a href="https://ngrx.io/" rel="noopener noreferrer"&gt;ngRx&lt;/a&gt; for Angular use it.&lt;/p&gt;

&lt;p&gt;React Context is a dependency injection solution, an alternative for &lt;a href="https://www.freecodecamp.org/news/prop-drilling-in-react-explained-with-examples/" rel="noopener noreferrer"&gt;prop-drilling&lt;/a&gt;. It's appropriate for simple state, like a boolean that represents whether the user is logged in. Conveniently, it's also the simplest, least abstracted example of the actions, dispatch, and reducers pattern.&lt;/p&gt;

&lt;p&gt;Once your state grows more complicated, then it's time to reach for a global state management library. A common example used in the docs for these libraries is an array of songs in a playlist and multiple API calls to get, add and remove songs from the playlist.&lt;/p&gt;

&lt;p&gt;This pattern has a lot of moving parts. I'll start by defining the parts, building towards a synchronous Context example. I'll rewrite that example in Redux and then add API calls. Finally, I'll recreate the same example with ngRX.&lt;/p&gt;

&lt;p&gt;You can find the React examples in the &lt;a href="https://github.com/abbeyperini/state-react" rel="noopener noreferrer"&gt;react-state GitHub repo&lt;/a&gt; and the Angular example in the &lt;a href="https://github.com/abbeyperini/state-angular" rel="noopener noreferrer"&gt;angular-state GitHub repo&lt;/a&gt;. I'm using &lt;a href="https://react.dev/learn/installation" rel="noopener noreferrer"&gt;React&lt;/a&gt; 19.1.0, &lt;a href="https://redux-toolkit.js.org/" rel="noopener noreferrer"&gt;Redux Toolkit&lt;/a&gt; 2.8.2, &lt;a href="https://angular.dev/" rel="noopener noreferrer"&gt;Angular&lt;/a&gt; 19.2.14 and &lt;a href="https://ngrx.io/docs" rel="noopener noreferrer"&gt;ngRx&lt;/a&gt; 19.2.1.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Immutability&lt;/li&gt;
&lt;li&gt;Actions&lt;/li&gt;
&lt;li&gt;Dispatch&lt;/li&gt;
&lt;li&gt;Reducers&lt;/li&gt;
&lt;li&gt;A Synchronous Example with React Context&lt;/li&gt;
&lt;li&gt;A Synchronous Example with Redux&lt;/li&gt;
&lt;li&gt;An Asynchronous Example with Redux&lt;/li&gt;
&lt;li&gt;A Synchronous Example with ngRx&lt;/li&gt;
&lt;li&gt;An Asynchronous Example with ngRx&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Immutability
&lt;/h2&gt;

&lt;p&gt;The actions, dispatch, and reducers pattern treats state as immutable. A JavaScript object is mutable. If I change part of the object, I have mutated it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// mutation&lt;/span&gt;
&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pauline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To treat a JavaScript object as immutable, I replace the entire object with an updated copy.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// not a mutation&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePerson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="c1"&gt;// make a copy&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPerson&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// apply the update to the copy&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;newPerson&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="c1"&gt;// return the copy&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;newPerson&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;updatePerson&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;firstName&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pauline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This doesn't just apply to objects. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach" rel="noopener noreferrer"&gt;&lt;code&gt;forEach&lt;/code&gt; array method&lt;/a&gt; will mutate your array. The &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/filter" rel="noopener noreferrer"&gt;&lt;code&gt;filter()&lt;/code&gt; array method&lt;/a&gt; will not mutate your array - it returns a copy.&lt;/p&gt;

&lt;p&gt;A lot of times, you'll see immutable state updates written with the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Spread_syntax" rel="noopener noreferrer"&gt;spread syntax&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;tasks&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;
  &lt;span class="nx"&gt;dishes&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;laundry&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;dusting&lt;/span&gt;
&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;addTask&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTask&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newTask&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;updatePersonName&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The spread syntax creates a copy and adds all of the old stuff that isn't being updated at the same time.&lt;/p&gt;

&lt;p&gt;Immutability sets the foundation for pure state update functions. Pure functions always return the same output when given the same inputs. Pure functions are free from side effects - they don't cause unintended consequences beyond the scope of the function. So state update functions should only update state, always update state the same way, and shouldn't affect things outside state.&lt;/p&gt;

&lt;h2&gt;
  
  
  Actions
&lt;/h2&gt;

&lt;p&gt;An action is an event that triggers changes to the state in the store. In the observer pattern, it'd be the notification to the observer that the subject has updated. You write the notifications that trigger effects now.&lt;/p&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%2Fkgroh9brwrsmsohmrovq.jpg" 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%2Fkgroh9brwrsmsohmrovq.jpg" alt="A black man pointing at his eyes with two fingers saying " width="596" height="586"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I want to build an app that allows the user to click a button to pet a shiba. It'll also show the shiba's reaction to the number pets. I'm not getting the pets from an asynchronous API call, you can't take away pets, and I'll only be giving one pet at a time. So to start, I only need one action - "PET".&lt;/p&gt;

&lt;p&gt;Actions are represented by strings. To prevent errors caused by misspelling a string, use a constant or &lt;a href="https://www.typescriptlang.org/docs/handbook/enums.html" rel="noopener noreferrer"&gt;enum&lt;/a&gt; for action strings.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In some cases, a string may provide all the information the store needs to update the state. Often, an action needs to contain additional information. When it does, the action string is the value of the &lt;code&gt;type&lt;/code&gt; property in an object. Any additional information is passed as the value of the &lt;code&gt;payload&lt;/code&gt; property.&lt;/p&gt;

&lt;p&gt;If I wanted to add more than 1 pet at a time, my action would look like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Dispatch
&lt;/h2&gt;

&lt;p&gt;In programming, dispatch is a synonym for send. In stores that use actions and reducers, you dispatch the action to the reducer. Thus, it is the name for the method that sends the action (notification) to the store.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Reducers
&lt;/h2&gt;

&lt;p&gt;When the store receives the action, what does it do with it? The reducer handles all the logic for setting/updating state.&lt;/p&gt;

&lt;p&gt;A reducer is a function that reduces a set of values to a single value. You may be familiar with JavaScript's &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/reduce" rel="noopener noreferrer"&gt;reduce&lt;/a&gt;. A reducer in the context of a store is similar, but not recursive.&lt;/p&gt;

&lt;p&gt;A store may have many possible actions. The reducer decides which action triggers which effect. In the observer pattern, it'd be the list registering effects to subjects. It can update state itself or call other functions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;create&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;handleRemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;handleUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;handleDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's a lot of &lt;code&gt;if&lt;/code&gt;s! Which is why you'll often see reducers written with a switch statement.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;switch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;add&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[...&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;];&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;remove&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;handleRemove&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;update&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;handleUpdate&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;case&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="k"&gt;delete&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="nf"&gt;handleDelete&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="nl"&gt;default&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
      &lt;span class="k"&gt;throw&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Error&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Not a valid action!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="k"&gt;break&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The switch statement also provides an easy way to catch malformed actions.&lt;/p&gt;

&lt;p&gt;The shiba petting app only has one action, so one &lt;code&gt;if&lt;/code&gt; statement will do.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;petsReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Side note: I'm using a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Increment#:~:text=If%20used%20prefix%2C%20with%20operator%20before%20operand%20(for%20example%2C%20%2B%2Bx)%2C%20the%20increment%20operator%20increments%20and%20returns%20the%20value%20after%20incrementing." rel="noopener noreferrer"&gt;pre-fixed increment operator&lt;/a&gt; here so that the value will be returned after it's been incremented. If I used &lt;code&gt;return pets++&lt;/code&gt;, I'd get the value before it's been incremented.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Synchronous Example with React Context
&lt;/h2&gt;

&lt;p&gt;&lt;a href="https://react.dev/learn/passing-data-deeply-with-context" rel="noopener noreferrer"&gt;For dependency injection alone, you can use a context without writing state management logic.&lt;/a&gt; My shiba petting app will have two components - &lt;code&gt;Pets&lt;/code&gt;, a counter with a button, and &lt;code&gt;Shiba&lt;/code&gt;, which will display the shiba's reaction to the pets. Since the &lt;code&gt;Shiba&lt;/code&gt; component doesn't update state, I can use dependency injection alone to pass the number of pets to it.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;createContext&lt;/code&gt; hook creates a context component that holds a value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PetContext.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PetContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumer components are children of the provider component. They use &lt;code&gt;useContext&lt;/code&gt; to consume the state in the context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PetContext.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// reusable wrapper function for useContext with a unique name&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PetContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumer components can be deeply nested and completely unrelated and still access the context in &lt;code&gt;&amp;lt;PetContext&amp;gt;&lt;/code&gt;. This would work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PetContext&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Pets&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
                  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Shiba&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
                &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
              &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
            &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
          &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PetProvider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Shiba.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;usePets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../PetContext.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Shiba&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="c1"&gt;// img tags and imports omitted for brevity&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;sadShiba&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;happyShiba&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;10&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;goodbyeShiba&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now the number of pets is passed to consumer components using a context, but the value is essentially hardcoded. I want the user to be able to update the number of pets with a button click, but right now it can only access the number of pets.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pets.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;usePets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../PetContext.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Pets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;??&lt;/span&gt;&lt;span class="p"&gt;?}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Pet&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Shiba&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The next step is writing the state management logic (getters and setters) with &lt;code&gt;useReducer&lt;/code&gt;. It's like manually writing the observer pattern on top of a dependency injection solution.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;useReducer&lt;/code&gt; returns two things - the current state and a dispatch method. A second context will hold the dispatch. &lt;code&gt;useReducer&lt;/code&gt; takes two arguments - the initial value for the state and a reducer. Both contexts, the action, and the reducer can all go in the same file.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PetContext.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useContext&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="c1"&gt;// create the contexts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PetContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;PetDispatchContext&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;PET&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// a wrapper component for both contexts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PetProvider&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;children&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;span class="c1"&gt;// pass the reducer and initial value to useReducer and get state and dispatch from the call&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;petsReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="c1"&gt;// pass the state and dispatch to the providers&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PetContext&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PetDispatchContext&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;children&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/PetDispatchContext&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/PetContext&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt; 
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// instantiate the reducer&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;petsReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PetContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;usePetDispatch&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;useContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;PetDispatchContext&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now wrapper component holds the state value and provides it and the update method (dispatch) to the consumer components.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// PetsApp.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;PetProvider&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../PetContext&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;PetsApp&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;PetProvider&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Pets&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Shiba&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/PetProvider&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;Pets&lt;/code&gt; component can access both contexts, so the user can update the number of pets with a button click.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Pets.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;usePetDispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../PetContext.jsx&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;Pets&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePets&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;petDispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;usePetDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;div&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;p&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;pets&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/p&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;      &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;button&lt;/span&gt; &lt;span class="nx"&gt;onClick&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;petDispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)}&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="nx"&gt;Pet&lt;/span&gt; &lt;span class="nx"&gt;the&lt;/span&gt; &lt;span class="nx"&gt;Shiba&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/button&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;    &lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="sr"&gt;/div&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;
&lt;/span&gt;  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F0gn111b71gi3okso7sfs.gif" 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%2F0gn111b71gi3okso7sfs.gif" alt="The number 0, a " width="600" height="663"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Any update within a context will trigger a re-render in every component consuming it - even if you're only updating one property and the consumer component isn't using that one property.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Synchronous Example with Redux
&lt;/h2&gt;

&lt;p&gt;This time, I want to build an app that counts shibas. I want the user to be able to add any number of shibas, not just one at a time.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ShibaCounter.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;React&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShibaCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleIncrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;setCount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Shibas: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleIncrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'number'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Number&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'number'&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add Shibas&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now that I've got my form, I want to move count out of local state and into global state. Why? In the next example, when the user updates the count, I want to trigger an API call to fetch that many shiba images. I need to get the synchronous half working first.&lt;/p&gt;

&lt;p&gt;I start by &lt;a href="https://redux-toolkit.js.org/introduction/getting-started" rel="noopener noreferrer"&gt;installing Redux Toolkit&lt;/a&gt;. Then, I need to instantiate a store for all of my global state. Redux creates one store. Like a pizza, one store can have many slices. Each slice holds a piece of state and its actions, dispatch, and reducers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// store.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;configureStore&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shibaCountReducer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./shibaCounterSlice&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nf"&gt;configureStore&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shibaCountReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Like Context, Redux uses a provider component to pass the store to consumer components with dependency injection. Where you'd want to keep your context as close as possible to the consumer components, a Redux store is meant to be global, so the provider is added at the root and wraps the whole app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// root.jsx&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;store&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Outlet&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Unlike Context, Redux uses a package called &lt;a href="https://immerjs.github.io/immer/" rel="noopener noreferrer"&gt;Immer&lt;/a&gt; to handle keeping state immutable.  I no longer have to handle copying the object to manually maintain the state I'm not updating.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// context&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;oldValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="c1"&gt;// redux&lt;/span&gt;
&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;key&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;newValue&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up, I need to create my first slice.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibaCounterSlice.js&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSlice&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@reduxjs/toolkit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibaCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;default&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Woah! That looks a bit different - what happened to the big switch statement? Because Redux is a state management library, it has syntax for creating actions and reducers. I don't have to write them manually.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;createSlice&lt;/code&gt; returns an object I've named &lt;code&gt;shibaCounterSlice&lt;/code&gt;. When I call &lt;code&gt;createSlice&lt;/code&gt;, I'm passing initial state and state update functions in the &lt;code&gt;reducers&lt;/code&gt; option object. &lt;code&gt;createSlice&lt;/code&gt; builds an &lt;code&gt;actions&lt;/code&gt; object with action creators made from those update functions. I then access those action creators by &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring" rel="noopener noreferrer"&gt;destructuring&lt;/a&gt; the &lt;code&gt;actions&lt;/code&gt; object.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// context&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// pass the action to dispatch&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// redux&lt;/span&gt;
&lt;span class="nl"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="o"&gt;++&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pets&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="c1"&gt;// creates an action creator&lt;/span&gt;
&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="c1"&gt;// consumer component passes the action creator to dispatch&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action creator consumes the action and automatically unpacks the payload.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// context&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="c1"&gt;// redux&lt;/span&gt;
&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The &lt;code&gt;useSelector&lt;/code&gt; hook selects a state from the store similar to how &lt;code&gt;useContext&lt;/code&gt; accessed the context. The &lt;code&gt;useDispatch()&lt;/code&gt; hook replaces the dispatch context. Now I can hook up the form to use the slice instead of local state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../shibaCounterSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShibaCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleIncrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F4txo0ifobjnkxf3472tb.gif" 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%2F4txo0ifobjnkxf3472tb.gif" alt="Shibas: 0. After 5 is typed into an input and the add shibas button is clicked, it shows Shibas: 5. The add shibas button is clicked twice more. It shows Shibas: 10 and then Shibas: 15." width="664" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Unlike &lt;code&gt;useContext&lt;/code&gt;, this component will only re-render when the state that is returned from &lt;code&gt;useSelector&lt;/code&gt; updates (e.g. shibaCount). Even if I could update other properties within the same slice, the component only listens for updates to what it's actually consuming. You can also leverage this to create &lt;a href="https://redux.js.org/usage/deriving-data-selectors" rel="noopener noreferrer"&gt;derived state&lt;/a&gt; to further improve performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  An Asynchronous Example with Redux
&lt;/h2&gt;

&lt;p&gt;Now, every time the user updates the count, I want to use the &lt;a href="https://dog.ceo/dog-api/" rel="noopener noreferrer"&gt;Dog API&lt;/a&gt; to show shiba pictures. You may be thinking "wait, an API call is definitely a side effect." And that's right! The reducers will still only update state. The state update will trigger middleware. The middleware will be an observer watching for updates to state. It's just a function that handles API calls and dispatches a new action with the fetched data.&lt;/p&gt;

&lt;p&gt;API calls can be pending, resolved, or rejected, so I'll need pending, success, and failure actions. Components can watch for these states and display a loading spinner, error, or the successfully fetched data. If you need to fetch data when the page loads, you can use an idle/initial state to indicate that the data hasn't been fetched yet.&lt;/p&gt;

&lt;p&gt;If I wrote this in redux alone, I'd start with my actions and reducers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibaCounterSlice.js&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;shibasPending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHIBAS-FETCH-PENDING&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shibaSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHIBAS-FETCH-SUCCESS&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;shibaFailure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;SHIBAS-FETCH-FAILURE&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibaCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaPending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaSuccess&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="na"&gt;shibaFetchFailure&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFailure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;shibaFetchFailure&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The reducer still only updates state! Next I need a middleware function that handles the API call and dispatches the appropriate actions.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibaCounterSlice.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;try&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://dog.ceo/api/breed/shiba/images/random/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;catch &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// use error action and bail out before a success is dispatched&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()));&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I could also write this with chaining.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;());&lt;/span&gt;
  &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://dog.ceo/api/breed/shiba/images/random/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;then&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;)),&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchFailure&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;toString&lt;/span&gt;&lt;span class="p"&gt;()))&lt;/span&gt;
  &lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Writing the same middleware with the exact same pattern for every fetch call gets tedious. That's why Redux Toolkit comes with &lt;a href="https://github.com/reduxjs/redux-thunk" rel="noopener noreferrer"&gt;redux-thunk&lt;/a&gt; middleware installed.&lt;/p&gt;

&lt;p&gt;A basic thunk is a &lt;a href="https://redux.js.org/usage/writing-logic-thunks#:~:text=about%2010%20lines.-,Here%27s%20the%20source,-%2C%20with%20additional%20added" rel="noopener noreferrer"&gt;wrapper function&lt;/a&gt; that consumes dispatch and a &lt;code&gt;getState&lt;/code&gt; function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;thunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getState&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;pet&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;5&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createAsyncThunk&lt;/code&gt; creates a thunk wrapper that handles the repetitive async logic.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibaCounterSlice.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;fetchShibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAsyncThunk&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shibaCounter/fetchShibas/&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;fetch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="s2"&gt;`https://dog.ceo/api/breed/shiba/images/random/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;code&gt;createAsyncThunk&lt;/code&gt; automatically defines pending, fulfilled, and rejected action strings for me. The first argument I passed is a string that will prepend them. So &lt;code&gt;fetchShibas.pending&lt;/code&gt; will be set to &lt;code&gt;"shibaCounter/fetchShibas/pending"&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Now that &lt;code&gt;createAsyncThunk&lt;/code&gt; is creating my actions and dispatching them for me, I need to let create &lt;code&gt;createSlice&lt;/code&gt; know. The &lt;code&gt;extraReducers&lt;/code&gt; essentially creates event listeners for the actions created outside of &lt;code&gt;createSlice&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibaCounterSlice.js&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSlice&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibaCount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;status&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;reducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCount&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;extraReducers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;builder&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;builder&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibasPending&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;fulfilled&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaSuccess&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;payload&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addCase&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;rejected&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFailure&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
        &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;})&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shibaCounterSlice&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;actions&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I'm ready to update the shiba counter component to call &lt;code&gt;fetchShibas&lt;/code&gt; when the count is updated. I use &lt;code&gt;useEffect&lt;/code&gt; (with all dependencies in my dependency array) to watch for an update and register the API call as an effect.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="c1"&gt;// ShibaCounter.jsx&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useEffect&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;useDispatch&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;react-redux&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;fetchShibas&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../shibaCounterSlice&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;ShibaCounter&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibaStatus&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaCounter&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useDispatch&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;

  &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;handleIncrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;preventDefault&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;parseInt&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTarget&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;elements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;shibaImages&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;shiba&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt; &lt;span class="na"&gt;src&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shiba&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt; &lt;span class="na"&gt;alt&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"shiba"&lt;/span&gt; &lt;span class="na"&gt;key&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;shiba&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;img&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;

  &lt;span class="nf"&gt;useEffect&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;fetchShibas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;

  &lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Shibas: &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;span&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt; &lt;span class="na"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nf"&gt;handleIncrement&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt; &lt;span class="na"&gt;htmlFor&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'number'&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Number&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;label&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;input&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;'number'&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"number"&lt;/span&gt;&lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
        &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="s"&gt;"submit"&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Add Shibas&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;button&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;form&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shibaStatus&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="nx"&gt;statusStrings&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibasPending&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;Pending&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;
      &lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shibaImages&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;div&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fb2ajqh6bghb13t9rwl2y.gif" 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%2Fb2ajqh6bghb13t9rwl2y.gif" alt="Same as the previous demo, but now every time 5 shibas are added to the shiba count, 5 shiba images are displayed. While the API call is pending, " width="651" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;In this example, I am counting shibas and displaying shiba images in the same component. In the real world, the benefit of using a global state solution is I could count in one component and display in a completely unrelated component.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Synchronous Example with ngRX
&lt;/h2&gt;

&lt;p&gt;Now I want to build the exact same example in Angular's ngRX. The concepts are the same, but the syntax is different. Once again, I'm going to start by getting the synchronous part working and then add in the API calls.&lt;/p&gt;

&lt;p&gt;After installing &lt;a href="https://ngrx.io/guide/store/install" rel="noopener noreferrer"&gt;@ngrx/store&lt;/a&gt; I need to provide the store to my app.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideStore&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;provideState&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideEffects&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shibasReducer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./state/shibas.reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;provideStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;provideEffects&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;provideState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shibas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shibasReducer&lt;/span&gt; &lt;span class="p"&gt;})]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The action creator method is aptly named &lt;code&gt;createAction&lt;/code&gt;. I have to define my action payload as props.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibas.actions.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createActionGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createAction&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emptyProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createAction&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Increment by Amount&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I need to write a selector to get the shiba feature out of my store - a store can have many features. To do that, I use &lt;code&gt;createFeatureSelector&lt;/code&gt; and pass the name I passed to &lt;code&gt;provideState&lt;/code&gt; in my app config. Once I have the whole feature, I can use &lt;code&gt;createSelector&lt;/code&gt; to get the value that I need.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/shibas.selector.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createFeatureSelector&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dogApi.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="nx"&gt;Count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectFeatureShibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createFeatureSelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shibas&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectShibaCount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectFeatureShibas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Count&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I import and instantiate the store in my top level App component. In my template, I pass the state value to the ShibaCounter component. I also set up an event listener. When my ShibaCounter component emits the &lt;code&gt;incrementByAmount&lt;/code&gt; event, my event listener will will access dispatch off of the store object and dispatch my &lt;code&gt;incrementByAmount&lt;/code&gt; action.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.component.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;selectShibaCount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./state/shibas.selectors&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./state/shibas.actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibaCounter&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shibas/shiba-counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;AsyncPipe&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;app-root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;main class="main"&amp;gt;
        &amp;lt;shiba-counter [count]="(count$ | async)!" (incrementByAmount)="onIncrementByAmount($event)" /&amp;gt;
      &amp;lt;/main&amp;gt;
    `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="nx"&gt;ShibaCounter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;AsyncPipe&lt;/span&gt; &lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AppComponent&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;

  &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;state-angular&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nx"&gt;count$&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

  &lt;span class="nf"&gt;onIncrementByAmount&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;Store&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;select&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectShibaCount&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then, I need a reducer. I pass two arguments to &lt;code&gt;createReducer&lt;/code&gt;. The first is initial state - 0. The second argument is an &lt;code&gt;on&lt;/code&gt; function. It creates an event listener! The first argument is the action to listen for and the second is a state update function.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/shibas.reducer.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./shibas.actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;shibasReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Finally, I want to consume and update the store from my ShibaCounter component. I get count as an input from my App component and use output to create the &lt;code&gt;incrementByAmount&lt;/code&gt; event listener.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibas/shiba-counter.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/forms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shiba-counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;span&amp;gt;Shibas: {{ count }}&amp;lt;/span&amp;gt;
    &amp;lt;form (ngSubmit)="onSubmit()"&amp;gt;
      &amp;lt;label for='number'&amp;gt;Number &amp;lt;/label&amp;gt;
      &amp;lt;input id='number' type="number" name="number" [formControl]="number"/&amp;gt;
      &amp;lt;button type="submit"&amp;gt;Add Shibas&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shiba-counter.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShibaCounter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2F4txo0ifobjnkxf3472tb.gif" 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%2F4txo0ifobjnkxf3472tb.gif" alt="Shibas: 0. After 5 is typed into an input and the add shibas button is clicked, it shows Shibas: 5. The add shibas button is clicked twice more. It shows Shibas: 10 and then Shibas: 15." width="664" height="148"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  An Asynchronous Example with ngRx
&lt;/h2&gt;

&lt;p&gt;Now it's time to add the API call to get shiba pictures to the previous example. First, I need to install &lt;a href="https://ngrx.io/guide/effects/install" rel="noopener noreferrer"&gt;@ngrx/effects&lt;/a&gt;. Then I need to update my app config providers. I need to provide the http client, effects, and new reducer. You can use &lt;a href="https://ngrx.io/guide/store/metareducers" rel="noopener noreferrer"&gt;meta-reducers&lt;/a&gt; to combine all of your reducers.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.config.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideHttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;provideEffects&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;dogApiReducer&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./state/dogApi.reducer&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="o"&gt;*&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="nx"&gt;ShibaEffects&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shibas/shibas.effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;appConfig&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ApplicationConfig&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;providers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nf"&gt;provideHttpClient&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;provideStore&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt; &lt;span class="nf"&gt;provideEffects&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShibaEffects&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nf"&gt;provideState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shibas&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;shibasReducer&lt;/span&gt; &lt;span class="p"&gt;}),&lt;/span&gt; &lt;span class="nf"&gt;provideState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;shibaData&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;reducer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;dogApiReducer&lt;/span&gt; &lt;span class="p"&gt;})]&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I'm made a type for my data, because it's not a primitive, and I don't want to type it out repeatedly.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/dogApi.model.ts&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kr"&gt;interface&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReadonlyArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;boolean&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="o"&gt;|&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next, I need pending, success, and failure actions for my API call. Since I have multiple actions this time, I use &lt;code&gt;createActionGroup&lt;/code&gt;. I'll be able to access them like &lt;code&gt;DogApiActions.shibaFetchPending&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/dogApi.actions.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createActionGroup&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;emptyProps&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;DogApiActions&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createActionGroup&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;source&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Dog API&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;events&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shiba Fetch Pending&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nf"&gt;emptyProps&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shiba Fetch Success&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ReadonlyArray&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Shiba Fetch Failure&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;props&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I need my selector. You can also use &lt;a href="https://ngrx.io/guide/store/selectors#using-selectors-for-multiple-pieces-of-state" rel="noopener noreferrer"&gt;selectors&lt;/a&gt; and &lt;a href="https://ngrx.io/guide/store/feature-creators" rel="noopener noreferrer"&gt;feature creators&lt;/a&gt; to combine multiple pieces of state.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/shibas.selectors.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./dogApi.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectFeatureShibaData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;createFeatureSelector&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;ShibaData&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shibaData&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;selectShibaData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createSelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;selectFeatureShibaData&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;state&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next up, the reducer.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// state/dogApi.reducer.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;createReducer&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;@ngrx/store&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DogApiActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dogApi.actions&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;./dogApi.model&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;dogApiReducer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createReducer&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="nx"&gt;initialState&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}),&lt;/span&gt;
  &lt;span class="nf"&gt;on&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFetchFailure&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;_state&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
    &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="nx"&gt;error&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Since my reducer should only update state, I need a service to handle the API call.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibas/shibas.service.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/common/http&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Injectable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Injectable&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;providedIn&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;root&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShibasService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kr"&gt;private&lt;/span&gt; &lt;span class="nx"&gt;http&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;HttpClient&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt;

  &lt;span class="nf"&gt;getShibas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt; &lt;span class="nx"&gt;Observable&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nb"&gt;Array&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;http&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="kd"&gt;get&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;Response&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;data&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt;&lt;span class="p"&gt;[]&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="s2"&gt;`https://dog.ceo/api/breed/shiba/images/random/&lt;/span&gt;&lt;span class="p"&gt;${&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;`&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;
      &lt;span class="p"&gt;}));&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now, I'm going to use I'm using @ngrx/effects and &lt;a href="https://rxjs.dev/" rel="noopener noreferrer"&gt;rxjs&lt;/a&gt; to write the observer pattern. rxjs is an Observables library for JavaScript that Angular uses. The effect returned from &lt;code&gt;createEffect&lt;/code&gt; will watch for the &lt;code&gt;DogApiActions.shibaFetchPending&lt;/code&gt; action. When it happens, &lt;code&gt;exhaustMap&lt;/code&gt; will pass the action to the &lt;code&gt;getShibas&lt;/code&gt; service, so I can grab the updated count from its props. Based on the result of the API call returned from &lt;code&gt;getShibas&lt;/code&gt;, &lt;code&gt;pipe&lt;/code&gt; will then dispatch the result in the appropriate action (&lt;code&gt;DogApiActions.shibaFetchSuccess&lt;/code&gt; or &lt;code&gt;DogApiActions.shibaFetchFailure&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibas/shibas.effects.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;inject&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Actions&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ofType&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@ngrx/effects&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;map&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;exhaustMap&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;catchError&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;rxjs/operators&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibasService&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shibas.service&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DogApiActions&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../state/dogApi.actions&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;getShibas&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createEffect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;actions$&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;Actions&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt; &lt;span class="nx"&gt;shibasService&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;ShibasService&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;actions$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
      &lt;span class="nf"&gt;ofType&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
      &lt;span class="nf"&gt;exhaustMap&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
        &lt;span class="nx"&gt;shibasService&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getShibas&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;action&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;pipe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
          &lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchSuccess&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;shibas&lt;/span&gt; &lt;span class="p"&gt;})),&lt;/span&gt;
          &lt;span class="nf"&gt;catchError&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;message&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;string&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt;
            &lt;span class="k"&gt;of&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchFailure&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;error&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;message&lt;/span&gt; &lt;span class="p"&gt;}))&lt;/span&gt;
          &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
      &lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;functional&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now I can add a subscription in my app component constructor that dispatches the &lt;code&gt;DogApiActions.shibaFetchPending&lt;/code&gt; action when &lt;code&gt;count&lt;/code&gt; updates. This is another way to write the observer pattern. If I wanted to fetch data on load, I would remove the &lt;code&gt;value &amp;gt; 0&lt;/code&gt; check. If I wasn't making the call based on an update to another state variable, I would dispatch an idle/initial action in &lt;a href="https://angular.dev/api/core/OnInit" rel="noopener noreferrer"&gt;&lt;code&gt;ngOnInit&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.component.ts&lt;/span&gt;
&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;count$&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;subscribe&lt;/span&gt;&lt;span class="p"&gt;((&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; 
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;&amp;gt;&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;store&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dispatch&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;DogApiActions&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;shibaFetchPending&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="na"&gt;count&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;}))&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;I also need to pass &lt;code&gt;shibaData&lt;/code&gt; to my ShibaCounter component in my template.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// app.component.ts&lt;/span&gt;
&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;shiba&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="nx"&gt;counter&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;count&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(count$ | async)!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;shibaData&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;(shibaData$ | async)!&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;onIncrementByAmount($event)&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In my ShibaCounter component, I grab &lt;code&gt;shibaData&lt;/code&gt; and display the shiba pictures, pending, and error states.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// shibas/shiba-counter.ts&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Output&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/core&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;FormsModule&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;@angular/forms&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;../state/dogApi.model&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Component&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;selector&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;shiba-counter&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;template&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;`
    &amp;lt;span&amp;gt;Shibas: {{ count }}&amp;lt;/span&amp;gt;
    &amp;lt;form (ngSubmit)="onSubmit()"&amp;gt;
      &amp;lt;label for='number'&amp;gt;Number&amp;lt;/label&amp;gt;
      &amp;lt;input id='number' type="number" name="number" [formControl]="number"/&amp;gt;
      &amp;lt;button type="submit"&amp;gt;Add Shibas&amp;lt;/button&amp;gt;
    &amp;lt;/form&amp;gt;
    @if (shibaData.pending) {
      &amp;lt;p&amp;gt;Pending&amp;lt;/p&amp;gt;
    }
    @if (shibaData.error) {
      &amp;lt;p&amp;gt;{{ shibaData.error }}&amp;lt;/p&amp;gt;
    }
    &amp;lt;div class="shiba-group"&amp;gt;
      @for (shiba of shibaData.shibas; track shiba) {
        &amp;lt;img [src]="shiba" alt="shiba" /&amp;gt;
      }
    &amp;lt;/div&amp;gt;
  `&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;styleUrl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./shiba-counter.css&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;imports&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;FormsModule&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;ReactiveFormsModule&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;span class="k"&gt;export&lt;/span&gt; &lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ShibaCounter&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;count&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Input&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;shibaData&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;ShibaData&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;error&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="na"&gt;shibas&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;@&lt;/span&gt;&lt;span class="nd"&gt;Output&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="nx"&gt;incrementByAmount&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nx"&gt;EventEmitter&lt;/span&gt;&lt;span class="o"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="o"&gt;&amp;gt;&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="nx"&gt;number&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;FormControl&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

  &lt;span class="nf"&gt;onSubmit&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;incrementByAmount&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;emit&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;number&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="o"&gt;!&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fb2ajqh6bghb13t9rwl2y.gif" 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%2Fb2ajqh6bghb13t9rwl2y.gif" alt="Same as the previous demo, but now every time 5 shibas are added to the shiba count, 5 shiba images are displayed. While the API call is pending, " width="651" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The actions, dispatch, and reducers pattern is difficult to wrap your head around at first. There are a lot of moving parts. My hope is that starting with the smallest possible example and slowly adding in concepts made the pattern easier to understand.&lt;/p&gt;

&lt;p&gt;The next part of the series will cover the mutator pattern used by global state management libraries like &lt;a href="https://zustand.docs.pmnd.rs/getting-started/introduction" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt; for React and &lt;a href="https://pinia.vuejs.org/" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt; for Vue (coming August 2025).&lt;/p&gt;

</description>
      <category>webdev</category>
      <category>redux</category>
      <category>angular</category>
      <category>react</category>
    </item>
    <item>
      <title>State Management in Front-end Web Development: State 101</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Mon, 02 Jun 2025 19:31:50 +0000</pubDate>
      <link>https://dev.to/abbeyperini/state-management-in-front-end-web-development-state-101-48g3</link>
      <guid>https://dev.to/abbeyperini/state-management-in-front-end-web-development-state-101-48g3</guid>
      <description>&lt;p&gt;Learn where to keep your variables to improve your app design, performance, and readability.&lt;/p&gt;

&lt;h2&gt;
  
  
  What is State?
&lt;/h2&gt;

&lt;p&gt;In programming, state refers to all the variables within a program. Variables represent data held in memory. &lt;/p&gt;

&lt;p&gt;Global state refers to variables that are globally scoped. They can be accessed from anywhere within the entire app. &lt;/p&gt;

&lt;p&gt;Local state refers to variables that are locally scoped. They can only be accessed within the file, component, or function where they are declared.&lt;/p&gt;

&lt;p&gt;Derived state refers to variables that are calculated based on other variables.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="c1"&gt;// displayName is derived state&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;When people talk about state in a front-end web app, they are typically referring to reactive state. Reactive state tracks updates and triggers effects when they happen. Reactive state management tools exist in all frameworks. Within them, a state variable is essentially a set of getters and setters. When you access the value, you're getting it. When you reassign the value, you're setting it. Tools that create reactive state will also have methods for instantiating the state variable, updating it, accessing its previous value, and more.&lt;/p&gt;

&lt;h2&gt;
  
  
  Local State Management
&lt;/h2&gt;

&lt;p&gt;When you are creating a local state variable, as yourself: &lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Does this variable need to be updated repeatedly?&lt;/li&gt;
&lt;li&gt;Do updates to this variable need to trigger updates somewhere else?&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If not, a regular &lt;code&gt;let&lt;/code&gt; or &lt;code&gt;const&lt;/code&gt; should suffice. Reactive state management tools are more powerful and will consume more resources. Why maintain a Ferrari when a bicycle will suit your needs?&lt;/p&gt;

&lt;p&gt;If you do need to update the variable and trigger effects based on updates, you'll want to create reactive state.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;React: &lt;a href="https://react.dev/learn/state-a-components-memory" rel="noopener noreferrer"&gt;useState&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vue: &lt;a href="https://vuejs.org/guide/essentials/reactivity-fundamentals.html" rel="noopener noreferrer"&gt;ref&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular: &lt;a href="https://angular.dev/essentials/signals" rel="noopener noreferrer"&gt;signals&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;In all three frameworks, you pass an initial value to a method. When you change the value, effects will be triggered. Anything that uses these reactive state variables will be notified when there's an update. So if you're displaying the value of a reactive state variable, the new value will be displayed.&lt;/p&gt;

&lt;p&gt;Frameworks also provide ways to trigger complex effects after an update. For example, a user clicks the "next page" button, so the value of your &lt;code&gt;pageNumber&lt;/code&gt; state variable is incremented, and you need to make an API call to get the next page of results. You'd use &lt;a href="https://react.dev/learn/extracting-state-logic-into-a-reducer" rel="noopener noreferrer"&gt;useReducer&lt;/a&gt; or &lt;a href="https://react.dev/reference/react/useEffect" rel="noopener noreferrer"&gt;useEffect&lt;/a&gt; in React, &lt;a href="https://vuejs.org/guide/essentials/watchers.html" rel="noopener noreferrer"&gt;watchers&lt;/a&gt; in Vue, and &lt;a href="https://angular.dev/guide/signals#effects" rel="noopener noreferrer"&gt;effects&lt;/a&gt; in Angular.&lt;/p&gt;

&lt;p&gt;In Angular, you can create reactive state using &lt;code&gt;signal&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;signal&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If you just want to assign a new value, you can use &lt;code&gt;set()&lt;/code&gt; to update your signal. If you want to compute a new value based on the old value, you use &lt;code&gt;update()&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;In Vue, you can create reactive state using &lt;code&gt;ref&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A ref maintains a reference to the original value and creates a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Proxy" rel="noopener noreferrer"&gt;Proxy&lt;/a&gt;. You access and reassign the value within the Proxy using &lt;code&gt;.value&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Pauline&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Person&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;14&lt;/span&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In React, you create a state value and an update function using the &lt;code&gt;useState&lt;/code&gt; hook. (A hook is a function that let you “hook into” React features within a functional component.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPerson&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Derived State Management
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React: &lt;code&gt;const&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Vue: &lt;a href="https://vuejs.org/guide/essentials/computed.html" rel="noopener noreferrer"&gt;computed&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular: &lt;a href="https://angular.dev/guide/signals#computed-signals" rel="noopener noreferrer"&gt;computed signals&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;When possible, keep derived state in the least powerful state tool.&lt;/p&gt;

&lt;p&gt;In React, it is more performative to declare derived state with &lt;code&gt;const&lt;/code&gt;. An update to a reactive state variable will trigger a component re-render. If you update one variable declared with &lt;code&gt;useState&lt;/code&gt; and trigger an update to another variable declared with &lt;code&gt;useState&lt;/code&gt;, the component will re-render twice. If you just use &lt;code&gt;const&lt;/code&gt;, the value will re-compute during the first re-render anyway.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Triggers a second re-render&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPerson&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;displayName&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setDisplayName&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Doesn't trigger a second re-render&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;setPerson&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;useState&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Paul&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Posey&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;12&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt; &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In Vue and Angular, this is when you'd use &lt;code&gt;computed()&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;displayName&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;computed&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;lastName&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;, &lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;value&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Put simply, whenever &lt;code&gt;person&lt;/code&gt; is updated, &lt;code&gt;displayName&lt;/code&gt; will update.&lt;/p&gt;

&lt;p&gt;Vue and Angular use the &lt;a href="https://en.wikipedia.org/wiki/Observer_pattern" rel="noopener noreferrer"&gt;observer pattern&lt;/a&gt; and keep track of which reactive variables trigger which effects. Instead of re-rendering the entire component, only the things that need to update will update.&lt;/p&gt;

&lt;p&gt;In observer pattern terms, the reactive variable &lt;code&gt;person&lt;/code&gt; is the subject. When we use &lt;code&gt;person&lt;/code&gt; in the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Callback_function" rel="noopener noreferrer"&gt;callback&lt;/a&gt; function passed to &lt;code&gt;computed()&lt;/code&gt;, the function is added to a list of observers. When the subject updates, the observers are notified, and the function will run again. This should sound familiar - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/EventTarget/addEventListener" rel="noopener noreferrer"&gt;&lt;code&gt;addEventListener()&lt;/code&gt;&lt;/a&gt; also uses the observer pattern. With &lt;code&gt;addEventListener()&lt;/code&gt;, the subject is an event and your listener (the event handler callback function you pass) is the observer/effect.&lt;/p&gt;

&lt;h2&gt;
  
  
  Dependency Injection
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React: &lt;a href="https://react.dev/learn/scaling-up-with-reducer-and-context" rel="noopener noreferrer"&gt;Context&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vue: &lt;a href="https://vuejs.org/guide/components/provide-inject" rel="noopener noreferrer"&gt;Provide/Inject&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular: &lt;a href="https://angular.dev/guide/di" rel="noopener noreferrer"&gt;Dependency Injection&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The more complex your application gets, the more &lt;a href="https://www.freecodecamp.org/news/prop-drilling-in-react-explained-with-examples/" rel="noopener noreferrer"&gt;prop-drilling&lt;/a&gt; becomes a problem. In a small application, it's fine to pass state down to child or even grandchild components. As a rule of thumb, if you have to pass state as a prop to a great-grandchild component, it's time to re-evaluate the design. It's common to run into this problem when multiple components need to use the same data from an API call.&lt;/p&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%2Fs2pw9d4eyqupured2qtf.jpeg" 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%2Fs2pw9d4eyqupured2qtf.jpeg" alt="A person labelled as a React Developer sweating and choosing between two buttons. One is labeled " width="500" height="756"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Before you reach for global state, consider an in-between solution - dependency injection. It's still local state, but instead of using props, parent components provide the state, and their children can consume it. That's why "dependency injection" and "provider/consumer" are used interchangeably.&lt;/p&gt;

&lt;p&gt;This pattern isn't just for reactive state. If you have derived state or a complex calculation, you can use dependency injection as a type of &lt;a href="https://en.wikipedia.org/wiki/Memoization" rel="noopener noreferrer"&gt;memoization&lt;/a&gt;. You only have to call an expensive function once and then you can inject the result.&lt;/p&gt;

&lt;p&gt;In Vue, a component uses the provide method to make a key value pair. Then, any child component can use the inject method to access that value.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Parent component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;ref&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nf"&gt;provide&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;





&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Child component&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;isLoggedIn&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;inject&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;key&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isLoggedIn&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="nf"&gt;showData&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You can inject update functions into consumer components. To avoid unintended side effects, updates should stay in the provider component.&lt;/p&gt;

&lt;p&gt;Angular also has an inject method for dependency injection. It provides multiple ways to create a provider. A common pattern is creating a class &lt;a href="https://angular.dev/tutorials/first-app/09-services" rel="noopener noreferrer"&gt;service&lt;/a&gt;, but you can also use &lt;a href="https://angular.dev/guide/di/dependency-injection-providers" rel="noopener noreferrer"&gt;values like a string, boolean, or Date&lt;/a&gt;. If you need to trigger updates from a consumer, your service class can define getters and setters.&lt;/p&gt;

&lt;p&gt;Dependency injection in React involves three hooks - &lt;code&gt;useReducer&lt;/code&gt;, &lt;code&gt;createContext&lt;/code&gt;, and &lt;code&gt;useContext&lt;/code&gt;. Writing a context requires understanding concepts that are used in global state management libraries. I'll explain how to write one in the next part of this series.&lt;/p&gt;

&lt;p&gt;Basically, &lt;code&gt;createContext&lt;/code&gt; creates a component to hold your state and &lt;code&gt;createContextDispatch&lt;/code&gt; creates a component with methods for updating your state.&lt;/p&gt;

&lt;p&gt;To use a context you've written, you need to wrap your components in a provider tag.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PersonContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PersonDispatchContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WelcomeBanner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MainContent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Footer&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TasksDispatchContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TasksContext&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Provider&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These days (v19.1), you no longer have to use &lt;code&gt;.Provider&lt;/code&gt; when using your context provider component.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PersonContext&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PersonDispatchContext&lt;/span&gt; &lt;span class="na"&gt;value&lt;/span&gt;&lt;span class="p"&gt;=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nx"&gt;dispatch&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;WelcomeBanner&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;MainContent&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;Footer&lt;/span&gt; &lt;span class="p"&gt;/&amp;gt;&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TasksDispatchContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;TasksContext&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Consumer child components use the &lt;code&gt;useContext()&lt;/code&gt; hook to access the state. Before the &lt;code&gt;useContext()&lt;/code&gt; hook was added, you had to use a consumer component to access your context.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight jsx"&gt;&lt;code&gt;&lt;span class="k"&gt;return &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nc"&gt;PersonDispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;(&lt;/span&gt;
     &lt;span class="p"&gt;&amp;lt;&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;person&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;firstName&lt;/span&gt; &lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nt"&gt;p&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;&amp;lt;/&lt;/span&gt;&lt;span class="nc"&gt;PersonDispatch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Consumer&lt;/span&gt;&lt;span class="p"&gt;&amp;gt;&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Global State Management
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;React: &lt;a href="https://redux.js.org/" rel="noopener noreferrer"&gt;Redux&lt;/a&gt;, &lt;a href="https://zustand.docs.pmnd.rs/getting-started/introduction" rel="noopener noreferrer"&gt;Zustand&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Vue: &lt;a href="https://pinia.vuejs.org/" rel="noopener noreferrer"&gt;Pinia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Angular: &lt;a href="https://ngrx.io/" rel="noopener noreferrer"&gt;ngRx&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What if you need to consume the same data and trigger the same API call to update that data in two unrelated components? It's common to run into this problem with auth (e.g. checking if the user has logged in). Global state is for variables that are used and updated in multiple unrelated files.&lt;/p&gt;

&lt;p&gt;Technically, you could make the argument that putting variables and update functions in your main file and using prop-drilling is global state management.&lt;/p&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%2F4f5i5qe5pneazyf4v7h6.jpg" 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%2F4f5i5qe5pneazyf4v7h6.jpg" alt="Smart guy meme - a black guy tapping the side of his head and smiling. Captioned " width="396" height="396"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;When we're talking about front-end web development, global state management usually refers to a library that makes your data available without the parent/child relationship required to prop-drill or use dependency injection. You should only use a global state management library when it's worth the performance trade-off.&lt;/p&gt;

&lt;p&gt;A global state management library typically creates a store. This pattern is so ubiquitous that sometimes global state management libraries refer to themselves as "a store." You store your state in a store. A store can maintain complex data structures. A store provides a standard way to make changes to your global state, making state changes predictable.&lt;/p&gt;

&lt;p&gt;There are a couple common patterns for writing stores and triggering updates in global state management libraries. I'll cover them in the next two parts of this series:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://dev.to/abbeyperini/state-management-in-front-end-web-development-actions-dispatch-and-reducers-gfo"&gt;State Management in Front-end Web Development: Actions, Dispatch, and Reducers&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;State Management in Front-end Web Development: Mutators (coming August 2025)&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>webdev</category>
      <category>react</category>
      <category>angular</category>
      <category>vue</category>
    </item>
    <item>
      <title>BenQ Programming Monitor Review</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Wed, 14 May 2025 22:22:09 +0000</pubDate>
      <link>https://dev.to/abbeyperini/benq-programming-monitor-review-2jif</link>
      <guid>https://dev.to/abbeyperini/benq-programming-monitor-review-2jif</guid>
      <description>&lt;p&gt;My first experience with a BenQ programming monitor was a hectic few hours filming the &lt;a href="https://www.youtube.com/watch?v=X2sEoZG8EIw&amp;amp;list=PLz8Iz-Fnk_eTkZvSNWXW_TKZ2UwVirT2M&amp;amp;index=2" rel="noopener noreferrer"&gt;Web Dev Challenge&lt;/a&gt;. The main things I noticed during that time were the rare 3:2 aspect ratio, lack of glare, and the convenient option to power my laptop with the monitor. I typically use my terminal in VS Code, so the aspect ratio allowed me to see more of my code while my terminal was open. The lack of glare was super important for filming.&lt;/p&gt;

&lt;p&gt;It's been about 8 weeks since I received my &lt;a href="https://www.benq.com/en-us/monitor/programming.html?utm_source=influencer_abbeyperini&amp;amp;utm_medium=social&amp;amp;utm_campaign=lcd_rd_rd280ua_trialprogram_202503_us" rel="noopener noreferrer"&gt;RD280UA monitor&lt;/a&gt; for my home office. Its adjustable arm mount was easy to attach to my desk. I like the fact that the Allen wrench has its own place in the mount, so you can't lose it. Immediately after setting it up, I noticed how bad the glare from the morning sun on my existing monitor had gotten.&lt;/p&gt;

&lt;p&gt;The next thing I noticed was the plethora of settings. There are several color modes and more color settings within them. You can even adjust the amount of red and green for red/green color blindness. If you can't choose a color mode, you can use two with DualView Plus.&lt;/p&gt;

&lt;p&gt;And then I got to the Eye Care menu. I've got &lt;a href="https://www.youtube.com/watch?v=V-fRuoMIfpw" rel="noopener noreferrer"&gt;special eyes&lt;/a&gt;.&lt;/p&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%2F349c8tmyr3wnbb78prkg.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%2F349c8tmyr3wnbb78prkg.png" alt="A man with eyes bulging as if his character model in the video game is broken" width="800" height="441"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many developers love dark mode because it helps with their eye strain. Thanks to an astigmatism, &lt;a href="https://www.allaboutvision.com/conditions/computer-vision-syndrome/digital-eye-strain/is-dark-mode-better-for-eyes/" rel="noopener noreferrer"&gt;I see halation and get headaches when I use a lot of dark modes&lt;/a&gt;. That's where this monitor shines - I have every tool I could possibly need to combat eye strain, not just a dark color with light text.&lt;/p&gt;

&lt;p&gt;It starts with the MoonHalo - it's a much better backlight than the one I've been using. The Brightness Intelligence will auto adjust and turn on Night Hours Protection based on ambient light. Low Blue Light Plus allows you to adjust the blue light to your preference. The Eye Reminder sends regular "take a break" notifications, something I have an app downloaded for. When I first set it up, I could feel my eyes relaxing as I adjusted everything. After a few days, I noticed my eyes were less tired at the end of the day.&lt;/p&gt;

&lt;p&gt;Right now, I've got three profiles setup - dev dark mode, dev light mode, and mbook (presumably MacBook). Using the Coding HotKey on the function bar, I can quickly switch between them. The main differences are brightness and temperature - mbook has none of the eye care settings turned on in case I need to evaluate colors.&lt;/p&gt;

&lt;p&gt;The main reason I chose the &lt;a href="https://www.benq.com/en-us/monitor/programming.html?utm_source=influencer_abbeyperini&amp;amp;utm_medium=social&amp;amp;utm_campaign=lcd_rd_rd280ua_trialprogram_202503_us" rel="noopener noreferrer"&gt;RD280UA&lt;/a&gt; over the RD280U was its adjustable arm. In addition to the special eyes, I've got special joints - ergonomics are a big deal for me. The adjustable arm is so easy to move and rotate. I can easily push it around, flip it, and tilt it to find the right position whether I'm sitting, standing, having to use my chair neck support a lot that day, or whatever.&lt;/p&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%2F25ibenao8stpjfmur4n9.jpg" 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%2F25ibenao8stpjfmur4n9.jpg" alt="Abbey's desk - the BenQ monitor showing code in a colorful theme, the ultrawide monitor showing the wildflower field from Howl's Moving Castle, blue, yellow, and white magical girl keycaps on a 70% white split keyboard with rainbow rgbs next to a white mouse on a matching desk mat, a custom PC filled with rgb lights in pink and blue, a macbook, a PS5 controller, a mic mounted on an arm, and pink headphones with ears on a kirby star stand, behind the desk is a metal grid covered in stickers, a plant hangs from the ceiling, embroidery art covers the wall next to it, beside a window, a chair sits in front of it, and a foot rest us underneath the desk in front of cords" width="800" height="680"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My setup requires flexibility beyond ergonomics too. My other monitor is a 34" ultrawide monitor. I use the same desk for my work MacBook Pro, my personal MacBook Pro, and my custom gaming PC. Luckily, this monitor is very flexible. You can daisy-chain multiple monitors with Multi-Stream Transport, multiple computers with the KVM switch, and multiple peripherals with the dedicated USB ports. I have a hub that I connect to several peripherals including a mouse, keyboard, external speakers, headphones, and more. Even though the monitor works with my hub, it has so many options that I'm still playing around with optimizing my setup, 8 weeks later.&lt;/p&gt;

&lt;p&gt;The only feature on the monitor itself that doesn't work as expected is the EcoPrivacy setting. It's supposed to turn the monitor off when you step away. It doesn't seem to sense me and immediately turns the monitor off.&lt;/p&gt;

&lt;p&gt;While double-checking some things for this review, I discovered &lt;a href="https://www.benq.com/en-us/support/downloads-faq/products/monitor/display-pilot-2/software-driver.html" rel="noopener noreferrer"&gt;Display Pilot 2&lt;/a&gt;. It adds an on screen display with various shortcuts. It also allows you to set up things like Display Partition and keyboard shortcuts. I like the idea of the flow feature. It's supposed to open up applications and perform various other actions on a schedule. I haven't tested it out yet. Display Partition only works on my M4 Pro MacBook and not my M2 Pro MacBook. The software crashed twice when I was initially setting it up on the M2 Pro MacBook. I haven't seen what will happen when settings are backed up from two different computers. Focus jumps randomly around a lot when using the software with a mouse, and it's unusable with just a keyboard or screen reader. Clearly, it's not integral to the monitor as I just discovered it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;If you want an incredibly configurable monitor with a rare aspect ratio, anti-reflection nano matte coating, and lots of anti-eye strain features, this is the monitor for you. The auto power off setting works fine in lieu of EcoPrivacy and I happily used it for 8 weeks without realizing there was companion software. &lt;/p&gt;

</description>
      <category>programming</category>
    </item>
    <item>
      <title>What the First Rule of ARIA Really Means</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Mon, 14 Apr 2025 14:00:51 +0000</pubDate>
      <link>https://dev.to/abbeyperini/what-the-first-rule-of-aria-really-means-192e</link>
      <guid>https://dev.to/abbeyperini/what-the-first-rule-of-aria-really-means-192e</guid>
      <description>&lt;p&gt;I am not an accessibility auditor, but I am frequently asked to give feedback on websites. When that happens, the first thing I do is check basic accessibility things. Multiple times, I have suggested an &lt;code&gt;aria-label&lt;/code&gt;, and heard "I thought I was never supposed to use ARIA."&lt;/p&gt;

&lt;p&gt;ARIA, the &lt;a href="https://www.w3.org/WAI/standards-guidelines/aria/" rel="noopener noreferrer"&gt;Accessible Rich Internet Applications Suite&lt;/a&gt;, is a huge topic and full of hard concepts. A lot of times, accessibility concepts are hard because the "right answer" is more grey than black. What may make something more accessible for one group of people may make things worse for another group. ARIA is hard because you need to learn those grey accessibility concepts, HTML in-depth, how to test the desired user experience with assistive technology, and ARIA.&lt;/p&gt;

&lt;p&gt;Learning it becomes even more intimidating when you hear &lt;a href="https://blog.pope.tech/2022/07/12/what-you-need-to-know-about-aria-and-how-to-fix-common-mistakes/#number-one-rule" rel="noopener noreferrer"&gt;"the first rule of ARIA is don't use ARIA"&lt;/a&gt;. This common adage doesn't mean ARIA will literally never make your webpage more accessible. It means two things - there's probably an HTML element for that and no ARIA is better than bad ARIA.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;ARIA Roles Are Semantics&lt;/li&gt;
&lt;li&gt;There's Probably an HTML Element for That&lt;/li&gt;
&lt;li&gt;No ARIA Is Better Than Bad ARIA&lt;/li&gt;
&lt;li&gt;
When to Use ARIA

&lt;ul&gt;
&lt;li&gt;Landmark Roles&lt;/li&gt;
&lt;li&gt;Label, Labelledby, or Describedby?&lt;/li&gt;
&lt;li&gt;Icons and Images That Convey Information&lt;/li&gt;
&lt;li&gt;Icon and Images That Don't Convey Information&lt;/li&gt;
&lt;li&gt;When HTML Elements Aren't Enough&lt;/li&gt;
&lt;li&gt;Announcing Additions to the Page&lt;/li&gt;
&lt;li&gt;Grouping Interactive Elements Outside of a Form&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;Learning Resources&lt;/li&gt;

&lt;li&gt;Conclusion&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;
  
  
  ARIA Roles Are Semantics
&lt;/h2&gt;

&lt;p&gt;Semantic HTML elements have semantic ARIA roles. The &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element will expose the role &lt;code&gt;button&lt;/code&gt; to the &lt;a href="https://developer.mozilla.org/en-US/docs/Glossary/Accessibility_tree" rel="noopener noreferrer"&gt;accessibility tree&lt;/a&gt;. Elements with the role &lt;code&gt;button&lt;/code&gt; will be announced by a screen reader.&lt;/p&gt;

&lt;p&gt;A generic HTML element, like a &lt;code&gt;&amp;lt;span&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;, has the ARIA role &lt;code&gt;generic&lt;/code&gt;. This role is not semantic. When exposed to the accessibility tree, it communicates "this is a nameless container element with no meaning." Unless it is focusable, an element's semantic role can be removed using &lt;code&gt;role="presentation"&lt;/code&gt; or &lt;code&gt;role="none"&lt;/code&gt;. Screen readers will not announce generic elements.&lt;/p&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%2F8wy5tqplxmivzratb2f2.jpg" 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%2F8wy5tqplxmivzratb2f2.jpg" alt="Homer Simpson standing on beer barrels, surrounded by a crowd, and giving the toast " width="640" height="480"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;So even an ARIA role that has no meaning is communicating something. Think of elements with the roles &lt;code&gt;generic&lt;/code&gt;, &lt;code&gt;presentation&lt;/code&gt;, and &lt;code&gt;none&lt;/code&gt; as metadata on a text document. The person reading the document doesn't need to know about the metadata for the text content of the document to make sense. In the same way, a screen reader user doesn't need to know that three elements are grouped together visually by a meaningless &lt;code&gt;&amp;lt;div&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The role of an element can even affect what information is communicated by the elements associated with it. The &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; element has a role of &lt;code&gt;contentinfo&lt;/code&gt;. If the browser sees an &lt;code&gt;&amp;lt;address&amp;gt;&lt;/code&gt; element as a child of an element with the &lt;code&gt;contentinfo&lt;/code&gt; role, it will be exposed to the accessibility tree as the contact address for the owner of the site.&lt;/p&gt;

&lt;h2&gt;
  
  
  There's Probably an HTML Element for That
&lt;/h2&gt;

&lt;p&gt;If you're adding an &lt;code&gt;onclick&lt;/code&gt; handler to a generic element, a semantic element probably exists with all the functionality you want to build (and a lot more you haven't thought about). If you aren't very familiar with how the HTML element works, replacing it with an ARIA role will rob you of functionality you didn't even know you needed.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Save&lt;span class="nt"&gt;&amp;lt;div/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;These things are true for the semantic &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;It is focusable.&lt;/li&gt;
&lt;li&gt;A focus outline will show when it is focused.&lt;/li&gt;
&lt;li&gt;It will activate on click, space, and enter.&lt;/li&gt;
&lt;li&gt;When activated and inside a form element, it will trigger a submit event (if it has no type attribute or &lt;code&gt;type="submit"&lt;/code&gt;).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is true for both the semantic &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element and the generic element with a &lt;code&gt;button&lt;/code&gt; role:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;The role &lt;code&gt;button&lt;/code&gt; will be exposed to the accessibility tree.&lt;/li&gt;
&lt;li&gt;A &lt;a href="https://sarahmhigley.com/writing/whats-in-a-name/" rel="noopener noreferrer"&gt;name&lt;/a&gt; for the element ("Save") will be exposed to the accessibility tree.&lt;/li&gt;
&lt;li&gt;A screen reader will announce "Button Save".&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;What you'd have to add to the generic element to make them equal:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;JavaScript handling click, space, and enter events&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;tabIndex="0"&lt;/code&gt; to make it focusable.&lt;/li&gt;
&lt;li&gt;If it's in a form element, JavaScript creating all the functionality you'd get from a submit event.&lt;/li&gt;
&lt;li&gt;Focus outline styling&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you just use &lt;code&gt;&amp;lt;div role="button"&amp;gt;&lt;/code&gt;, a screen reader will announce the element as a button, but the user will get none of the functionality that comes with a &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;h2&gt;
  
  
  No ARIA Is Better Than Bad ARIA
&lt;/h2&gt;

&lt;p&gt;Until you learn how to use ARIA correctly, &lt;a href="https://webaim.org/projects/million/#aria" rel="noopener noreferrer"&gt;using it will likely make things worse&lt;/a&gt;. ARIA attributes come with requirements. If you don't know those requirements, you'll start cluttering up your code with useless attributes and creating invalid elements that cause headaches for users.&lt;/p&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%2Fk3bs4j4s22bbi6dot9c6.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%2Fk3bs4j4s22bbi6dot9c6.png" alt="A full-stack developer pouring a huge bottle of oil onto a salad. The oil is labelled " width="800" height="886"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Many roles require specific attributes and children elements be used with them. Returning to the button example - &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Roles/button_role#required_javascript_features:~:text=Required%20event%20handlers" rel="noopener noreferrer"&gt;the spec requires&lt;/a&gt; that you write &lt;code&gt;onclick&lt;/code&gt; and &lt;code&gt;onkeydown&lt;/code&gt; handlers when you use &lt;code&gt;role="button"&lt;/code&gt;. We should use &lt;code&gt;&amp;lt;input type="checkbox"&amp;gt;&lt;/code&gt;. But if we used &lt;code&gt;role="checkbox"&lt;/code&gt;, we'd also have to use &lt;code&gt;aria-labelledby&lt;/code&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-checked" rel="noopener noreferrer"&gt;&lt;code&gt;aria-checked&lt;/code&gt;&lt;/a&gt;. Then we'd have to build all the functionality the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; element already has and keep the value of &lt;code&gt;aria-checked&lt;/code&gt; up to date. &lt;/p&gt;

&lt;p&gt;Some roles' requirements prohibit you from using certain attributes and elements as children. You can't use any element as a child of the &lt;code&gt;&amp;lt;ul&amp;gt;&lt;/code&gt; element - it has to be a &lt;code&gt;&amp;lt;li&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; or &lt;code&gt;&amp;lt;template&amp;gt;&lt;/code&gt; element. &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Guides/Content_categories#interactive_content" rel="noopener noreferrer"&gt;Interactive content&lt;/a&gt; can't be a child of the &lt;code&gt;&amp;lt;button&amp;gt;&lt;/code&gt; element. The values you can pass to &lt;code&gt;aria-checked&lt;/code&gt; depend on the element on which it's used. You can't use &lt;code&gt;aria-checked&lt;/code&gt; on a button, and you can't use &lt;a href="https://w3c.github.io/aria/#aria-pressed" rel="noopener noreferrer"&gt;&lt;code&gt;aria-pressed&lt;/code&gt;&lt;/a&gt; on a checkbox.&lt;/p&gt;

&lt;p&gt;If you've been learning semantic HTML, some of this should sound familiar. A lot of the rules you learn about how to use HTML elements are ARIA under the hood.&lt;/p&gt;

&lt;h2&gt;
  
  
  When to Use ARIA
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Landmark Roles
&lt;/h3&gt;

&lt;p&gt;If you only learn one part of ARIA, it should be the special subset of roles called &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/landmark_role" rel="noopener noreferrer"&gt;landmark roles&lt;/a&gt;. Assistive technologies expect these roles in a page and generate a page summary based on them. Screen readers provide users with multiple ways to navigate a page - like a list of landmarks.&lt;/p&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%2Fa7yxui7hsn1tmmtq6aua.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%2Fa7yxui7hsn1tmmtq6aua.png" alt="the typical website layout - header element role=" width="577" height="482"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The typical website layout uses &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt;, &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;, and &lt;code&gt;&amp;lt;footer&amp;gt;&lt;/code&gt; because they have the landmark roles &lt;code&gt;banner&lt;/code&gt;, &lt;code&gt;main&lt;/code&gt;, and &lt;code&gt;contentinfo&lt;/code&gt;. These three roles can't be duplicated - you can only have one per page. Additionally, you have to keep them separate - a &lt;code&gt;&amp;lt;header&amp;gt;&lt;/code&gt; element loses the role &lt;code&gt;banner&lt;/code&gt; if it is a child of an element with another landmark role, like &lt;code&gt;&amp;lt;main&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;The &lt;code&gt;&amp;lt;nav&amp;gt;&lt;/code&gt; element has the landmark role &lt;code&gt;navigation&lt;/code&gt;. If you provide a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; with a &lt;a href="https://sarahmhigley.com/writing/whats-in-a-name/" rel="noopener noreferrer"&gt;name&lt;/a&gt;, it gains the &lt;code&gt;form&lt;/code&gt; landmark role. Some landmark roles, like &lt;code&gt;navigation&lt;/code&gt; and &lt;code&gt;form&lt;/code&gt;, can be used multiple times per page. But if you use more than one, screen readers users need to know how they are different. Don't make them read every link in your navigation menu to find out. Use &lt;code&gt;aria-labelledby&lt;/code&gt; or &lt;code&gt;aria-label&lt;/code&gt; to provide them with unique names. The same concept applies to other elements with landmark roles like &lt;code&gt;&amp;lt;section&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;aside&amp;gt;&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you use HTML elements with landmark roles appropriately, a screen reader user can use the list of landmarks like a &lt;a href="https://webaim.org/techniques/skipnav/" rel="noopener noreferrer"&gt;skip link&lt;/a&gt;. With very little effort, you save them from the tedium of navigating through the same header content every time they navigate to one of your pages. Using the list of landmark roles, they'll be able to jump right to your named forms without having to skim the entire page. Once they're done filling out your form, they can jump right back to the navigation menu the same way.&lt;/p&gt;

&lt;h3&gt;
  
  
  Label, Labelledby, or Describedby?
&lt;/h3&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%2F19a6jrowdnw8b6304pnz.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%2F19a6jrowdnw8b6304pnz.png" alt="Oprah saying " width="800" height="1278"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The attributes &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label" rel="noopener noreferrer"&gt;&lt;code&gt;aria-label&lt;/code&gt;&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-labelledby" rel="noopener noreferrer"&gt;&lt;code&gt;aria-labelledby&lt;/code&gt;&lt;/a&gt; are used to give an element a name that can be read by a screen reader. They can't be used on &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-label#associated_roles" rel="noopener noreferrer"&gt;some elements&lt;/a&gt;, including those with role &lt;code&gt;presentation&lt;/code&gt; or &lt;code&gt;none&lt;/code&gt;. You can't provide a name for an element that has a role that by definition doesn't have a name. Read Sarah Higley's &lt;a href="https://sarahmhigley.com/writing/whats-in-a-name/" rel="noopener noreferrer"&gt;What's in a name?&lt;/a&gt; for a full explanation of element names.&lt;/p&gt;

&lt;p&gt;Elements that need a name typically generate that name themselves from associated text content. So every time you reach for ARIA to add a name to an element, ask yourself why you aren't using text inside the element or an associated &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; element.&lt;/p&gt;

&lt;p&gt;If you can't use either of those, but there is text already on the page, use &lt;code&gt;aria-labelledby&lt;/code&gt;. You just pass it an &lt;a href="https://w3c.github.io/aria/#valuetype_idref_list" rel="noopener noreferrer"&gt;ID Reference List&lt;/a&gt; (one or more IDs separated by a comma). Any ID in the ID Reference List should correspond to the &lt;code&gt;id&lt;/code&gt; attribute of an element containing the text you want to use.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;h3&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"color-heading"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Favorite Color&lt;span class="nt"&gt;&amp;lt;/h3&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"color-heading"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;As a last resort, you can pass a name as a string to &lt;code&gt;aria-label&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"expand menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&amp;gt;&lt;/span&gt;Right Chevron Icon Code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You also pass an ID Reference List to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-describedby" rel="noopener noreferrer"&gt;&lt;code&gt;aria-describedby&lt;/code&gt;&lt;/a&gt;, but it doesn't provide the element with a name. Instead, it provides a description. A name should be concise - a few words. A description can be multiple sentences. This attribute is a way to link pertinent text information from another element. Use &lt;code&gt;aria-describedby&lt;/code&gt; in situations where helpful text is near the element. If you find yourself putting an icon next to an interactive control and expecting a user to know to hover or click to learn more, that's the perfect time to use &lt;code&gt;aria-describedby&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt; &lt;span class="na"&gt;aria-describedby=&lt;/span&gt;&lt;span class="s"&gt;"warning"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Self Destruct&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;p&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"warning"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Only click this button if you don't want an account anymore.&lt;span class="nt"&gt;&amp;lt;/p&amp;gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Warning Icon Code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;In the (extremely rare) situation that you need to pass a description as a string instead of an ID, you'd use &lt;code&gt;aria-description&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Let's say you have so much information about the element that the user would have to interact with another element or elements to get the information (e.g. clicking a link, expanding a &lt;code&gt;&amp;lt;details&amp;gt;&lt;/code&gt; element, or reading text in multiple &lt;code&gt;&amp;lt;p&amp;gt;&lt;/code&gt; elements).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-details=&lt;/span&gt;&lt;span class="s"&gt;"explanation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Self Destruct&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"/self-destruct-explanation"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"explanation"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Learn more about self destruct&lt;span class="nt"&gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;By passing the ID of the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element to the &lt;code&gt;aria-details&lt;/code&gt; attribute, we associate the &lt;code&gt;&amp;lt;a&amp;gt;&lt;/code&gt; element with the button. In other words, screen reader users won't get text, they'll be pointed towards the link itself. Assistive technologies will even use the role of the elements referenced in the ID Reference List to provide more information to the user.&lt;/p&gt;

&lt;h3&gt;
  
  
  Icons and Images That Convey Information
&lt;/h3&gt;

&lt;p&gt;If you're using icons to convey information, you need to give screen reader users access to that information. For example, an icon that tells users that the link opens in a new tab. If the icon is a static image, you can use the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/HTMLImageElement/alt" rel="noopener noreferrer"&gt;alt&lt;/a&gt; attribute. If it's an SVG and you can't edit or dynamically update the SVG code, that &lt;em&gt;could&lt;/em&gt; be an appropriate time to use &lt;code&gt;role="img"&lt;/code&gt; to give it a role so that you can use &lt;code&gt;aria-label&lt;/code&gt; to give it a unique &lt;a href="https://sarahmhigley.com/writing/whats-in-a-name/" rel="noopener noreferrer"&gt;name&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--Link with "external link" icon--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;a&amp;gt;&lt;/span&gt;Check out the GitHub Repo&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"opens external link"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;External Link Icon Code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There are &lt;a href="https://www.smashingmagazine.com/2021/05/accessible-svg-patterns-comparison/" rel="noopener noreferrer"&gt;many solutions for this problem&lt;/a&gt;. This is a good a place as any to note - robust testing inclusive of people with disabilities is the only way to determine that your website has an accessible user experience.&lt;/p&gt;

&lt;p&gt;It's always better to have a visible label for interactive controls. Still, we often see buttons and links with an image or SVG icon and no text. Those elements &lt;a href="https://sarahmhigley.com/writing/whats-in-a-name/#:~:text=How%20do%20you%20name%20an%20element%3F" rel="noopener noreferrer"&gt;generate a name&lt;/a&gt; based on text content and ARIA attributes - not images. You can use &lt;code&gt;aria-label&lt;/code&gt; to give them a unique name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"expand menu"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt; &lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Right Chevron Icon Code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Links must always have content, so the &lt;code&gt;aria-label&lt;/code&gt; has to go on the image.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;a&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;"https://github.com/repo"&lt;/span&gt; &lt;span class="na"&gt;target=&lt;/span&gt;&lt;span class="s"&gt;"_blank"&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;"noreferrer"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"img"&lt;/span&gt; &lt;span class="na"&gt;aria-label=&lt;/span&gt;&lt;span class="s"&gt;"open Github repository in a new tab"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;GitHub logo code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/a&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Icons and Images That Don't Convey Information
&lt;/h3&gt;

&lt;p&gt;On the flip side, a screen reader user probably doesn't need to hear about every icon on your page. (You may have noticed the &lt;code&gt;aria-hidden&lt;/code&gt; attribute in the previous button example.) If a static image doesn't communicate information, use &lt;code&gt;alt=""&lt;/code&gt;. If it's an SVG, you can use &lt;code&gt;aria-hidden="true"&lt;/code&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--"Add +" button--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;button&amp;gt;&lt;/span&gt;Add&lt;span class="nt"&gt;&amp;lt;svg&lt;/span&gt; &lt;span class="na"&gt;aria-hidden=&lt;/span&gt;&lt;span class="s"&gt;"true"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;+ Icon Code&lt;span class="nt"&gt;&amp;lt;/svg&amp;gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  When HTML Elements Aren't Enough
&lt;/h3&gt;

&lt;p&gt;If you are building an interactive element for which no HTML exists, then you want to use ARIA. This is rarer than people think. It's one of the main reasons why the first rule of ARIA is don't use ARIA. "The &lt;code&gt;&amp;lt;select&amp;gt;&lt;/code&gt; element looks ugly" doesn't fall into this category. Developers are constantly reinventing the wheel when it comes to interactive elements.&lt;/p&gt;

&lt;p&gt;Still, it does happen. I have had to build a &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/treeview/" rel="noopener noreferrer"&gt;tree view&lt;/a&gt; with &lt;a href="https://dev.to/abbeyperini/nodeiterator-and-treewalker-web-apis-278p"&gt;NodeIterator and Treewalker&lt;/a&gt;, and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/tablist_role" rel="noopener noreferrer"&gt;tablists&lt;/a&gt; are pretty common. If you're building a component functionality triggered by user actions, use ARIA to tell screen reader users about the actions available to them.&lt;/p&gt;

&lt;p&gt;The simplest, most common example is a checkbox that checks or unchecks all the checkboxes below it.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;fieldset&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Dogs&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;aria-controls=&lt;/span&gt;&lt;span class="s"&gt;"dog1,dog2,dog3,dog4"&lt;/span&gt; &lt;span class="na"&gt;aria-checked=&lt;/span&gt;&lt;span class="s"&gt;"mixed"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Select All&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;ul&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"checkboxes"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dog1"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Shiba Inu&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dog2"&lt;/span&gt; &lt;span class="na"&gt;checked=&lt;/span&gt;&lt;span class="s"&gt;""&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Corgi&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dog3"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Husky&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;li&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;label&amp;gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"checkbox"&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"dog4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Mutt&lt;span class="nt"&gt;&amp;lt;/label&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/li&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/ul&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;How do you communicate to a screen reader user that the "select all" checkbox controls the other checkboxes? You pass an ID Reference List to &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Attributes/aria-controls" rel="noopener noreferrer"&gt;&lt;code&gt;aria-controls&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;How do you communicate to a screen reader user that only some of the checkboxes controlled by the "select all" checkbox are checked? &lt;a href="https://www.w3.org/WAI/ARIA/apg/patterns/checkbox/examples/checkbox-mixed/" rel="noopener noreferrer"&gt;&lt;code&gt;aria-checked="mixed"&lt;/code&gt;&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Announcing Additions to the Page
&lt;/h3&gt;

&lt;p&gt;How does a blind user know when new text has been added to the page or they get a notification or when you show an error banner? You'll need to use live regions created with &lt;code&gt;aria-live&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;To learn more, read &lt;a href="https://www.sarasoueidan.com/blog/accessible-notifications-with-aria-live-regions-part-2/?utm_campaign=coschedule&amp;amp;utm_source=twitter&amp;amp;utm_medium=EqualizeDigital" rel="noopener noreferrer"&gt;Sara Soueidan's extensive guide to ARIA live regions&lt;/a&gt; and my &lt;a href="https://dev.to/abbeyperini/live-regions-in-react-4dmd"&gt;quick primer on using them in React&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Grouping Interactive Elements Outside of a Form
&lt;/h3&gt;

&lt;p&gt;Within a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element, a &lt;a href="https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/fieldset" rel="noopener noreferrer"&gt;&lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt;&lt;/a&gt; element gives you useful functionality. Paired with a &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt; element, you automatically format your form data and get a semantic name.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--Will have a border--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;fieldset&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;legend&amp;gt;&lt;/span&gt;Themes&lt;span class="nt"&gt;&amp;lt;/legend&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Light&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Dark&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;input&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"radio"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Rainbow&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Outside of a &lt;code&gt;&amp;lt;form&amp;gt;&lt;/code&gt; element, you don't need that functionality and often have to override the styling that comes with a &lt;code&gt;&amp;lt;fieldset&amp;gt;&lt;/code&gt; and &lt;code&gt;&amp;lt;legend&amp;gt;&lt;/code&gt;. Instead, you can use the role &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/group_role" rel="noopener noreferrer"&gt;&lt;code&gt;group&lt;/code&gt;&lt;/a&gt;.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!--Won't have a border--&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;role=&lt;/span&gt;&lt;span class="s"&gt;"group"&lt;/span&gt; &lt;span class="na"&gt;aria-labelledby=&lt;/span&gt;&lt;span class="s"&gt;"styling"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;id=&lt;/span&gt;&lt;span class="s"&gt;"styling"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Text Styling&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Bold&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Italics&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"button"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;Remove Formatting&lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/fieldset&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;You'll have to remember to use &lt;code&gt;aria-labelledby&lt;/code&gt; or &lt;code&gt;aria-label&lt;/code&gt;, but you won't have to override the styling. This can be helpful if you have to build interactive components like &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/toolbar_role" rel="noopener noreferrer"&gt;toolbars&lt;/a&gt;, &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/menu_role" rel="noopener noreferrer"&gt;menus&lt;/a&gt; and &lt;a href="https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Reference/Roles/menubar_role" rel="noopener noreferrer"&gt;menubars&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Learning Resources
&lt;/h2&gt;

&lt;p&gt;This is by no means an exhaustive lesson in ARIA. If you'd like to learn more, here are a few places to start:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.w3.org/WAI/standards-guidelines/aria/" rel="noopener noreferrer"&gt;WAI-ARIA Overview&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.smashingmagazine.com/2022/09/wai-aria-guide/" rel="noopener noreferrer"&gt;Making Sense of WAI-ARIA&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://developer.mozilla.org/en-US/docs/Learn/Accessibility/WAI-ARIA_basics" rel="noopener noreferrer"&gt;WAI-ARIA Basics&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.youtube.com/watch?app=desktop&amp;amp;si=X6EvD4L8iBTDS63j&amp;amp;v=kbwe7ab7-tg&amp;amp;feature=youtu.be&amp;amp;ab_channel=AfricaKenyah" rel="noopener noreferrer"&gt;Africa Kenyah's Intro to ARIA&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I hope this helps some people get over their fear of ARIA. Accessibility advocates say "The first rule of ARIA is don't use ARIA" because they constantly have to fix the giant mess caused by developers using ARIA attributes that they don't understand.&lt;/p&gt;

</description>
      <category>a11y</category>
      <category>webdev</category>
      <category>html</category>
      <category>frontend</category>
    </item>
    <item>
      <title>If They'll Adopt It, It's Right</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Fri, 28 Mar 2025 19:33:07 +0000</pubDate>
      <link>https://dev.to/abbeyperini/if-theyll-adopt-it-its-right-4152</link>
      <guid>https://dev.to/abbeyperini/if-theyll-adopt-it-its-right-4152</guid>
      <description>&lt;p&gt;It's no secret Andy Bell, Alex Riviere, and I love CSS. Alex recently wrote &lt;a href="https://alex.party/posts/2025-03-23-grid-first-flex-third/" rel="noopener noreferrer"&gt;Grid First, Flex Third&lt;/a&gt;. (I found the part about how some of us are rewriting &lt;code&gt;display: block&lt;/code&gt; with &lt;code&gt;display: flex&lt;/code&gt; especially interesting.) Then &lt;a href="https://piccalil.li/blog/if-it-works-its-right/" rel="noopener noreferrer"&gt;Andy wrote a response&lt;/a&gt; eschewing absolutes and talking about how some people will always prefer flex over grid. (His &lt;a href="https://piccalil.li/blog/cube-css/" rel="noopener noreferrer"&gt;CUBE system&lt;/a&gt; is similar to how I write CSS when left to my own devices.)&lt;/p&gt;

&lt;p&gt;Neither of them are wrong. Both of them have many years of experience and enjoy thinking about how the stylesheets cascade. If you enjoy thinking about the same kind of thing, you should go read both articles.&lt;/p&gt;

&lt;p&gt;Andy's main point is absolutely correct.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;If it works, it's right. - &lt;a href="https://rachelandrew.co.uk/" rel="noopener noreferrer"&gt;Rachel Andrew&lt;/a&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;His CUBE CSS article touches on another important point - the challenge is getting people to use whatever system you come up with. He mentions how people often think a single tool, like Tailwind, will be the silver bullet for a styling system for a large project.&lt;/p&gt;

&lt;p&gt;Alex is my boss so I know anger and frustration are typically what drive him to write. I also know what code he's angry about. Alex and I have been repeatedly hitting a point where we can't update something without completely restyling it. We've been trading off ripping out components and page structures and rewriting them. The main issue is that there are tools (e.g. Tailwind and component libraries), but no system. Everything is copy and pasted ooze. (I propose ooze as a term for something that's less organized than soup.)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- ooze --&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-2 w-full"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex flex-col flex-1 w-4/5"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-2 flex-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border border-blue bg-grey flex rounded-lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
         View More
         &lt;span class="nt"&gt;&amp;lt;img&lt;/span&gt; &lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"~/assets/down-caret"&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"w-24 h-24"&lt;/span&gt; &lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
       &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border border-blue bg-[#cccccc] flex rounded-lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        Cancel
      &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-2 flex-2"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;div&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"flex gap-4"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
         &lt;span class="nt"&gt;&amp;lt;button&lt;/span&gt; &lt;span class="na"&gt;class=&lt;/span&gt;&lt;span class="s"&gt;"border border-blue bg-blue flex rounded-lg"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
           Apply
         &lt;span class="nt"&gt;&amp;lt;/button&amp;gt;&lt;/span&gt;
        &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/div&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To Andy's point, he'd still be angry if it was CSS grid ooze.&lt;/p&gt;

&lt;p&gt;In past articles (&lt;a href="https://dev.to/abbeyperini/from-idea-to-design-for-non-designers-m6d"&gt;From Idea to Design for Non-Designers&lt;/a&gt; and &lt;a href="https://piccalil.li/blog/tips-on-extensible-and-maintainable-components/" rel="noopener noreferrer"&gt;Tips on Extensible and Maintainable Components article&lt;/a&gt; that I wrote with Andy), I've talked about how any software project is a living system. Every tool has trade-offs. Updates and new requirements will happen. You need to be able to jump back into it even if you haven't touched it 6 months or years.&lt;/p&gt;

&lt;p&gt;I agree with Andy and Alex - we should be thinking about how to use CSS (or any tool) most effectively. It seems to me that developers are often keen to examine trade-offs, refactor, and measure things like readability with most programming languages. When it comes to CSS, many panic and throw that out the window. Part of it is that CSS works differently than any other programming language they've used. But a lot of it is the lack of intentionality most teams have with their styling tool of choice. This isn't the first codebase I've worked in where I've had to overhaul the styling just to get some work done.&lt;/p&gt;

&lt;p&gt;I'm sure the developers before us could have made a useable styling system with Tailwind. I do think Tailwind and other CSS frameworks add extra hurdles when you're trying to style intentionally for large projects. The way they are written encourages developers to copy and paste their styling unintentionally. And to make an intentional, easy to adopt system with them requires the same work you'd do with CSS on top of wresting with the framework itself. (Even on a small project, you'll need to override &lt;a href="https://github.com/tailwindlabs/tailwindcss/issues/8961" rel="noopener noreferrer"&gt;inaccessible choices&lt;/a&gt;, which means you'll still need to know CSS.)&lt;/p&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%2F9h61ljoaky2i6lnykhcz.jpeg" 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%2F9h61ljoaky2i6lnykhcz.jpeg" alt="Gru, a villain, presenting his evil plan - learn to create a website, add HTML to it, and add CSS to it. Then " width="640" height="410"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Early in my career, I had already fallen in love with CSS before I was told I should hate it. I also had the honor of working with one of the smartest people I'll ever work with. He was mainly a back-end developer who didn't want to think about CSS. Usually, we would pair and I would build the front-end portion. On one project we worked on together, I logged in hours before a deadline to see that a PR had been merged with a brand-new, full-stack feature. I rushed to look at how he had styled it. It was exactly how I would have styled it. Later that day, he told me "That was so easy to style." I've been chasing that high ever since.&lt;/p&gt;

&lt;p&gt;What made it so easy to style? I'd love to say it was that we were using grid, block, and flex in CSS in the right amounts. In reality, it was the same kinds of things people recommend for any other programming language.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;If we found it was repeated 3 times, we'd make a reusable class.&lt;/li&gt;
&lt;li&gt;We gave classes concise, human-readable names.&lt;/li&gt;
&lt;li&gt;Our stylesheets were heavily commented.&lt;/li&gt;
&lt;li&gt;Our stylesheets were grouped by functionality.&lt;/li&gt;
&lt;li&gt;We tried to keep our styling consistent across pages.&lt;/li&gt;
&lt;li&gt;We tried to avoid overriding styles, &lt;code&gt;!important&lt;/code&gt;, or anything that generated inconsistent results.&lt;/li&gt;
&lt;li&gt;We tried to use local scope over global scope where possible.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It's the right styling system if someone who doesn't want to know CSS can adopt it easily. Use every tool in your toolbox intentionally and effectively. Write your code so that you in 6 months or the back-end dev will immediately understand it.&lt;/p&gt;

&lt;p&gt;If they'll adopt it, it's right.&lt;/p&gt;

</description>
      <category>css</category>
      <category>webdev</category>
      <category>programming</category>
      <category>designpatterns</category>
    </item>
    <item>
      <title>Cancer Treatment Sucks</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Tue, 18 Mar 2025 15:31:30 +0000</pubDate>
      <link>https://dev.to/abbeyperini/cancer-treatment-sucks-3ccp</link>
      <guid>https://dev.to/abbeyperini/cancer-treatment-sucks-3ccp</guid>
      <description>&lt;p&gt;&lt;a href="https://dev.to/abbeyperini/cancer-sucks-1e2i"&gt;When we left off&lt;/a&gt;, I had just met with the first of three oncologists. I did end up with a specialist because there are so few patients like me. When you take into account my existing connective tissue disorder, I'm even rarer.&lt;/p&gt;

&lt;p&gt;And that's the common thread throughout my cancer treatment journey. Patients like me are so rare that healthcare professionals struggled to hide their surprise. They sincerely and reflexively blessed my heart. (If you're not from the South, a sincere "bless your heart" is when you know it's really bad.) My rarity stemmed mainly from my age. I joked receptionists kept looking for the grandmother I had to be accompanying. Cancer statistics usually end at 15 years after diagnosis. That'll get me to 47. The first two oncologists wanted to treat me like I was 47.&lt;/p&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%2F9fyk27hbl4med6zqkkma.jpg" 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%2F9fyk27hbl4med6zqkkma.jpg" alt="Having cancer gave me membership in an elite club I'd rather not belong to - Gilda Radner" width="800" height="800"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My rarity also stemmed from not having worse cancer. I was on a (relatively) light regimen. I struggled in cancer group settings - I learned too much about how much worse it could be. I felt obligated to give support instead of receiving it. I was more comfortable navigating doctors and jargon and pain than most. Doctors were surprised by my energy and questions and ecstatic about how well I was healing compared to most (older) patients.&lt;/p&gt;

&lt;p&gt;While I navigated an egg retrieval for &lt;a href="https://www.mayoclinic.org/tests-procedures/in-vitro-fertilization/about/pac-20384716#:~:text=A%20desire%20to%20preserve%20fertility%20due%20to%20cancer%20or%20other%20health%20conditions" rel="noopener noreferrer"&gt;onco-fertility preservation&lt;/a&gt;, I felt truly alone. Most people who brave the onslaught of needles and invasive tests and intense hormones are planning on having a kid as soon as possible. I was freezing embryos just in case treatment affected my fertility. If I choose to use them, I'll be pausing my treatment. It wouldn't increase the chances my cancer will come back, but the chances will never be 0. A doctor told me no one had a good answer for a question I never asked - how can I bring a kid into this world knowing my cancer might come back? I tried not to feel guilty about being able to afford it (with the help of grants like &lt;a href="https://www.teammaggiesdream.org/" rel="noopener noreferrer"&gt;Team Maggie's Dream&lt;/a&gt;).&lt;/p&gt;

&lt;p&gt;A friend assured me that feeling like doom for exactly three hours was a normal reaction to one of the drugs. On the way to an appointment, I found out that my uncle had suffered a stroke. I stood in the fertility wing of the hospital and watched the sun rise further over the surrounding woods. I ordered flowers for my aunt. He passed away soon after. I cried about many things on that ten day hormonal roller coaster.&lt;/p&gt;

&lt;p&gt;At the end of the process, I vibed with my pregnant friends who would give birth a week later. However, they knew their bundles would bring joy. The end of my journey was the beginning of chemo. It was ironically easier to get to my procedures from my con hotel room. So as I stripped off my Stupid Sexy Gandalf costume to let my husband stab me with the final dose, I felt alone in a crowd of about 80,000. I woke up frequently that night - apparently a possible side effect of that last shot is extreme anxiety.&lt;/p&gt;

&lt;p&gt;The procedure went extremely well. I returned to con that night, feeling on top of the world. A few weeks later, I spoke at &lt;a href="https://www.youtube.com/watch?v=hSotPlaVxjg&amp;amp;ab_channel=Netlify" rel="noopener noreferrer"&gt;Netlify Compose&lt;/a&gt;. Both events helped me get a little distance from the cancer and make friends who would help me through the next part.&lt;/p&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%2F8fqdx6ol2hyes0finpl9.jpg" 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%2F8fqdx6ol2hyes0finpl9.jpg" alt="A selfie of Abbey and friends in the Fairmont San Francisco ballroom that Netlify Compose was held in" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I've had plenty of mental health struggles in my life, but navigating chemo while going through menopause was harrowing. The hot flashes alone threatened my sanity. In about four months, my body went through two years worth of egg making and then three months without hormones. The steroids and chemo only added weight, bloat, and face-swelling. A doctor asked me why I gained weight and why I was masking. I cried in my car. I threw out his referral and called a doctor a fellow radiation patient recommended instead.&lt;/p&gt;

&lt;p&gt;People told me I looked great (for a cancer patient). I struggled with hair loss and could barely shower. I kept a normal amount of hair, thanks to cold capping and having extremely thick hair to begin with. I could be a cold capping brand ambassador.&lt;/p&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%2F9h52i6xyfivs3telciio.jpg" 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%2F9h52i6xyfivs3telciio.jpg" alt="Abbey in the infusion chair, wrapped in blankets in a hat that makes her look like an old timey fighter pilot." width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a result, people had no concept of how much hair I lost. It's like my hair was my enemy and a friend I was losing at the same time. While my scalp froze during and after infusions, I reminded myself that having hair gave me a sense of normalcy. Laying on the couch at home, my hair parting ways with my aching scalp by the handfuls felt anything but normal. Constantly tangled in it, I rivaled Wesley, the shiba inu, in creating fur dust bunnies around the house. I'm still pulling clothing covered in hair out of the dryer.&lt;/p&gt;

&lt;p&gt;The hair around your eyes is on a longer growth cycle. A month after chemo ended, I felt I had adjusted to the hair loss. Then, my eyebrows and eyelashes fell out. I bought a bunch of makeup. I began following video tutorials by people with alopecia to draw on my eyebrows. We all missed our eyelashes the most.&lt;/p&gt;

&lt;p&gt;Now, I'm watching all my hair grow back in, but slightly different. It feels like I shaved my entire body and the stubble is growing back in at a snail's pace.&lt;/p&gt;

&lt;p&gt;Every infusion, I thought "I have to do this &lt;em&gt;again&lt;/em&gt;" in terror. Every infusion, I thought "This is a &lt;em&gt;light&lt;/em&gt; regimen." in despair. Every infusion, I wondered if the horrible nerve pain or bone pain would start. In three out of four infusions, they struggled to get an IV in me. I learned about inter-departmental needle drama. I almost considered a port.&lt;/p&gt;

&lt;p&gt;I had to think this would work. I couldn't think about doing it again if the cancer came back. I began to feel nauseous in cars, elevators, and when I thought about the next infusion. My husband and I joked about "double milk milkshakes." We both wondered if I would be so unable to eat at some point that I would need them.&lt;/p&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%2F48d12795ptwy6n98kccq.jpg" 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%2F48d12795ptwy6n98kccq.jpg" alt="A picture of Frodo laying in Sam's lap on Mount Mordor captioned I no longer recall the taste of strawberries" width="625" height="276"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I explained the procedures to my loved ones - full of cancer "fun" facts. I became a lightning rod for people's cancer feelings and news. I drowned in paperwork and bills, unmedicated for ADHD for the first time in years. I languished in pain and a myriad of discomforts, each one robbing me of a little more dignity than the last. I hyperfocused on birds and everyone thought that seemed healthy.&lt;/p&gt;

&lt;p&gt;After the first infusion, I travelled to Raleigh for &lt;a href="https://2024.allthingsopen.org/" rel="noopener noreferrer"&gt;All Things Open&lt;/a&gt;. It was there, struggling to give my talk, that I realized I was going to have to reckon with my pride and internalized ableism. My sister basically did everything but walk me through the TSA line to get me on the plane home. In the line, I debated whether she should have walked me through it. It took me 40 minutes to wind my way through the infinity symbol shaped path, and I wondered what would happen if I passed out.&lt;/p&gt;

&lt;p&gt;At home the next day, I was happy to be able to hand out candy on Halloween, my favorite holiday. After word got out about my cancer, my neighbors had dropped off care packages. The houses around me had more Halloween decorations than previous years. People loved our Mothman and lightbulb costumes. I almost felt normal.&lt;/p&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%2Fut4x2nv6p8e8mga3a2fg.jpg" 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%2Fut4x2nv6p8e8mga3a2fg.jpg" alt="Abbey dressed as Mothman and her husband wearing a lightbulb hat" width="800" height="1280"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;A few weeks later, I managed to make it to &lt;a href="https://2024.connect.tech/" rel="noopener noreferrer"&gt;Connect.Tech&lt;/a&gt; for a few hours. Immediately, it was palpable that people knew I was a cancer patient. I hated the sad looks, but not people offering me chairs and insisting I let them fetch things for me.&lt;/p&gt;

&lt;p&gt;While I was struggling with feeling left out and unable to celebrate Christmas like I normally would, my grandmother fell and injured herself badly. She would be in and out of the ICU for the next two months. I reckoned with yet another layer of grief wrapped in complicated family history.&lt;/p&gt;

&lt;p&gt;Eventually, I succumbed and began doing whatever I wanted. I learned who dug in when the going got tough and who expected me to still cater to them. I leaned into relationships with the people who dug in. I learned to trust, relax, and prioritize myself - skills which had historically evaded me. Slowly, the brain fog prevented me from thinking. The fatigue became so heavy that I struggled to wake up. Once awake, I regretted not being asleep.&lt;/p&gt;

&lt;p&gt;That winter, everything felt wrong. I was a rock, stuck in the silt, as the river of everyone else's life moved around and past me. I watched, helpless, as my husband swam against the current to pull me a centimeter or two forward. 2/2 counselors agree we navigated it well. We were together and alone in our struggles.&lt;/p&gt;

&lt;p&gt;As I rang the bell signaling the end of my chemo treatment and the ward cheered, I felt like a sack of emotion and poison. The day of my last infusion was the day the radiation treatment planning began. Each time they lined me up in the radiation machine, they applied more and more permanent marker and stickers. "Instead of tattoos!" the told me cheerfully. If x marks the spot, I was covered in treasure. It's a good thing I didn't run into any pirates. Showering acquired another level of difficulty.&lt;/p&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%2Fhji3juog0pb7krlgzihy.jpg" 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%2Fhji3juog0pb7krlgzihy.jpg" alt="A girl rushing somewhere captioned my tastebuds are back for a week! where's the food!" width="750" height="499"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As my energy returned, I still felt overwhelmed by the daily appointments. As I got used to them, my energy began to wane again. The laser beam didn't hurt. The subsequent sunburn did. Despite the radiation, my overall health slowly improved. I could walk the dog without feeling like I was going to pass out.&lt;/p&gt;

&lt;p&gt;In the patient-only waiting room in the back, we discussed how many appointments we had completed and how many we had yet to complete. I heard about people driving 2 hours one way because this location was the only option their insurance gave them. Many had to stay in the &lt;a href="https://www.cancer.org/support-programs-and-services/patient-lodging/hope-lodge.html" rel="noopener noreferrer"&gt;Hope Lodge&lt;/a&gt;. (I try not to feel guilty about receiving cutting-edge care so close to my home.) Fellow patients looked relieved when I brought up my age first. Everyone complimented my hair.&lt;/p&gt;

&lt;p&gt;Every Friday during most of chemo and all of radiation, I wore my new blanket coat to the other hospital to get acupuncture. It didn't not help when intimidating security guards complimented it. I reasoned with my needle phobia that these were stabby needles, like tattoos. I used my honed ability of Not Thinking About It when it asked what happens if one breaks.&lt;/p&gt;

&lt;p&gt;The best visit was when my husband had time to visit the hospital aviary with me. His work urged him to make up the hours he was spending taking me to appointments. The burnout prevented him from caring. State law protected his right to use his sick time. Through &lt;a href="https://www.mealtrain.com/" rel="noopener noreferrer"&gt;MealTrain&lt;/a&gt;, I was finally able to take some of the burden off of him.&lt;/p&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%2Fv31bw0najthstgurje3c.jpg" 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%2Fv31bw0najthstgurje3c.jpg" alt="A pug lays on a blue deck. His front legs are extended towards his butt so his face is laying on the deck. Captioned tired just tired." width="625" height="352"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;I made cookies for the staff whose sarcastic banter and smiles helped me through radiation. Even though the laser beam doses were over, it did not feel over. I still had daily doctors appointments. I hoped the fatigue wouldn't get much worse. Two days later, my husband and I went out to dinner for Valentine's Day. I dressed up. It was nice. At home, I peeled the clothes off my abused skin gingerly. Like right after chemo, congratulations poured in. Like right after chemo, I wondered when it would feel over.&lt;/p&gt;

&lt;p&gt;Six days after radiation ended, I was in a meeting when I found out my grandmother had passed away. For once in my life, I cancelled everything and let myself feel. Well, everything but getting pretty nails again. I chose a design that felt hopeful. I'd missed my nail artist. We watched Jurassic Park and I almost felt normal.&lt;/p&gt;

&lt;p&gt;After a couple weeks, the fatigue began to lift again. I leapt back into life. I cling to the little good things. I Don't Think About the statistics. I take the new symptoms, like scar tissue forming from radiation, as they come. I contemplate plastic surgery and kids. My relationship with my body, myself, and everyone in my life is different. It feels like it is over and it isn't. I got a week's respite before I started the drug I'll be on for five to ten years. So far, it seems like I'm tolerating it well.&lt;/p&gt;

&lt;p&gt;My veins are still bruised and scarred from chemo. It's now been long enough that I can say I probably won't get the horrible nerve pain side effect. I plan on cutting my hair short next week. I'm a little softer around the edges and little harder in other places.&lt;/p&gt;

</description>
    </item>
    <item>
      <title>We've Been Here Since the Beginning: 2025 Edition</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Fri, 07 Mar 2025 18:39:34 +0000</pubDate>
      <link>https://dev.to/abbeyperini/weve-been-here-since-the-beginning-2025-edition-90</link>
      <guid>https://dev.to/abbeyperini/weve-been-here-since-the-beginning-2025-edition-90</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/wecoded"&gt;WeCoded Challenge&lt;/a&gt;: Echoes of Experience&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://dev.to/abbeyperini/weve-been-here-since-the-beginning-2nnp"&gt;Last time&lt;/a&gt;, I was tired. This time, I'm angry.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;I'm Angry&lt;/li&gt;
&lt;li&gt;The Black Women Who Made Margaret Hamilton's Work Possible&lt;/li&gt;
&lt;li&gt;Molly Holzschlag&lt;/li&gt;
&lt;li&gt;Alan Emtage&lt;/li&gt;
&lt;li&gt;Mary Ann Horton&lt;/li&gt;
&lt;li&gt;Roy Clay Sr.&lt;/li&gt;
&lt;li&gt;Edith Windsor&lt;/li&gt;
&lt;li&gt;Conclusion&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  I'm Angry
&lt;/h2&gt;

&lt;p&gt;Today, &lt;a href="https://en.wikipedia.org/wiki/Doublespeak" rel="noopener noreferrer"&gt;doublespeak&lt;/a&gt; about DEI (diversity, equity, and inclusion) is all the rage in America. According to those in power, it's the cause of plane crashes and inefficiency. They crow that it needs to be pulled out by the roots so merit-based hiring is possible.&lt;/p&gt;

&lt;p&gt;In reality, civil rights are inconvenient if you want to consolidate wealth in one combination of race, class, and gender. DEI has been an often slow and painful effort to move the needle a little bit towards merit-based hiring. This is especially obvious when you notice how much of the attack on DEI is focused on erasing history.&lt;/p&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%2Fxmqdxnuct12wwelk3wyo.jpeg" 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%2Fxmqdxnuct12wwelk3wyo.jpeg" alt="Huw @huwbrews Just got the call. Fired. I was the DEI officer at the bicycle shop. They're just calling them cycles from now on." width="599" height="599"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;People feel safe telling me that I had an easier time getting a job because of DEI. They believe &lt;a href="https://www.linkedin.com/pulse/quota-myth-just-wont-die-gregg-ward-oxric/" rel="noopener noreferrer"&gt;the quotas myth&lt;/a&gt;. When it works, DEI gives us small benefits that aim to level the playing field - like having to interview more job candidates than just the hiring manager's fraternity brothers. Usually, it doesn't work. Thanks to the DEI efforts of those who came before me, I've actually had a job where I wasn't the only femme-presenting technical person in the room. Now, many companies are openly trying to &lt;a href="https://www.thecut.com/article/mark-zuckerberg-thinks-companies-need-masculine-energy.html" rel="noopener noreferrer"&gt;reverse that trend&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;My anger grows when I think about how I have it pretty easy. Though I am queer, I pass as &lt;a href="https://www.verywellmind.com/what-does-cishet-mean-5219425" rel="noopener noreferrer"&gt;cishet&lt;/a&gt;. People are socialized to see my lack of melanin as non-threatening. Generational wealth and connections give me more opportunities and a safety net. I've had support whenever I've had to stand up for myself in the face of discrimination and harassment.&lt;/p&gt;

&lt;p&gt;Diversity and inclusion don't stop at race, gender, and class, so accessibility is under attack too. It's no secret that that's something I'm &lt;a href="https://dev.to/abbeyperini/web-development-accessibility-f8i"&gt;passionate about&lt;/a&gt;. Some of that is personal experience - I take on physical and cognitive barriers every day of my life. Accommodations I need are not won easily. Mostly, it's empathy. Over and over again, I have found that empathy makes me a better developer. The results of that empathy - accessibility and inclusion - make better systems. And yet, I can only fume as I watch industry leaders choose money over people. I am stuck seething as people I look up to are told to erase their accessibility work or lose their job.&lt;/p&gt;

&lt;p&gt;We took the short-term view with the Cavendish banana. Its &lt;a href="https://apsjournals.apsnet.org/doi/10.1094/PHYTO-07-20-0311-RVW" rel="noopener noreferrer"&gt;lack of genetic diversity&lt;/a&gt; means the global banana industry collapse grows more likely every day. In the same way, a lack of diversity of thought in tech will rob us of a more sustainable, more human future. We can combat that by listening to those who see the negative impacts of technology the most - like Amazon Alexa engineer Michael Running Wolf's work &lt;a href="https://www.indigenouswatchdog.org/update/how-ai-can-help-indigenous-language-revitalization-and-why-data-sovereignty-is-important/" rel="noopener noreferrer"&gt;trying to revitalize indigenous languages&lt;/a&gt;. Let's start by acknowledging that the kinds of people they're trying to erase have been and always will be integral to advances in programming and web development.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Black Women Who Made Margaret Hamilton's Work Possible
&lt;/h2&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%2Fvop2hg7pvo70ahfvtevz.jpeg" 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%2Fvop2hg7pvo70ahfvtevz.jpeg" alt="Evelyn Boyd Granville, a Black woman in pearls and a striped shirt, smiling at the camera" width="250" height="326"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: mathshistory.st-andrews.ac.uk&lt;/p&gt;

&lt;p&gt;&lt;a href="https://mathematicallygiftedandblack.com/honorees/evelyn-boyd-granville/" rel="noopener noreferrer"&gt;Evelyn Boyd Granville&lt;/a&gt; was the second Black woman to receive a Ph.D. in mathematics from an American university. &lt;a href="https://mathshistory.st-andrews.ac.uk/Biographies/Granville/" rel="noopener noreferrer"&gt;Academic posts for Black women were highly restrictive&lt;/a&gt;, so she explored other options. In 1956, she was working as a computer programmer at IBM. She started out writing programs in the assembly language SOAP and FORTRAN for the IBM 650 computer. In 1957, as part of IBM’s Vanguard Computing Center, she wrote programs that tracked orbits for the Vanguard satellite and Mercury spacecraft programs. In 1962, Evelyn joined the aerospace firm North American Aviation. This time, she was working on celestial mechanics and trajectory calculations for the Apollo project.&lt;/p&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%2Fcc0po9ex70fa6j2a4wwe.jpeg" 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%2Fcc0po9ex70fa6j2a4wwe.jpeg" alt="Dorothy Johnson Vaughan, a Black woman with her hair pulled back and wearing a blazer, in black and white, looking at the camera" width="800" height="1070"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: nasa.gov&lt;/p&gt;

&lt;p&gt;In 1957, &lt;a href="https://www.biography.com/scientists/dorothy-johnson-vaughan" rel="noopener noreferrer"&gt;Dorothy Johnson Vaughan&lt;/a&gt; was manager of the &lt;a href="https://www.nasa.gov/people/dorothy-vaughan/" rel="noopener noreferrer"&gt;National Advisory Committee for Aeronautics’ (NACA’s) West Area Computing Unit&lt;/a&gt;. She had been managing the segregated unit since 1949. While NASA (National Aeronautics and Space Administration) wasn't even called NASA yet, she was the first Black woman promoted to a managerial role. During the 10 years she managed the unit, Dorothy and the Black women who reported to her contributed to virtually every area of research at Langley. When engineers found that they had a challenging computing task, they would ask Dorothy to handle it personally. In that role, she took it upon herself to advocate for her direct reports and other women who were passed over for raises and promotions. &lt;/p&gt;

&lt;p&gt;In 1958, the space race was heating up. This is when NACA desegregated, abolished teams separated by gender, and became NASA. Dorothy joined the new Analysis and Computation Division (ACD). The book and movie &lt;a href="https://www.scientificamerican.com/article/the-story-of-nasas-real-ldquo-hidden-figures-rdquo/" rel="noopener noreferrer"&gt;&lt;em&gt;Hidden Figures&lt;/em&gt;&lt;/a&gt; covered events in 1961 and 1962.&lt;/p&gt;

&lt;p&gt;As part of her work for the ACD, Dorothy learned FORTRAN and contributed to the &lt;a href="https://artsandculture.google.com/story/nasa-s-unsung-hero-the-scout-launch-vehicle-program-u-s-national-archives/BAURgejPzIk_KQ?hl=en" rel="noopener noreferrer"&gt;Scout Launch Vehicle Program&lt;/a&gt;. This program aimed to make reliable rocket launches possible. It created the launch system used by many U.S. and international space missions and programs. This includes the first American satellites and the &lt;a href="https://en.wikipedia.org/wiki/Interplanetary_Monitoring_Platform" rel="noopener noreferrer"&gt;Interplanetary Monitoring Platform (IMP)&lt;/a&gt;. The IMP collected data on spatial and temporal relationships of geophysical and interplanetary phenomena like radiation. The Apollo program required the technological advances made to get the system working and the data collected by it. &lt;/p&gt;

&lt;p&gt;Although Dorothy never received another managerial role, Dorothy's legacy lived on through the success of those she supported while she managed them.&lt;/p&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%2Fmswgx916ffgd6s4t83va.jpeg" 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%2Fmswgx916ffgd6s4t83va.jpeg" alt="Mary Jackson, a Black woman with her hair styled short and curly wearing a white jacket, in black and white, looking away from the camera" width="800" height="936"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: nasa.gov&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.nasa.gov/people/mary-w-jackson-biography/" rel="noopener noreferrer"&gt;Mary Jackson&lt;/a&gt; became the first Black woman to be promoted to engineer. For a long time, she was probably the only one at NASA. To work in the 4-foot by 4-foot Supersonic Pressure Tunnel, she had to get special permission to attend the required training classes. They were held in a segregated high school. After a couple decades and 12 papers on understanding air flow, including thrust and drag forces, she was awarded the Apollo Achievement Award. When she realized she wasn't going to get promoted anymore, she took a demotion. Her new position had impact on the hiring and promotion of the next generation of all of NASA’s women mathematicians, engineers and scientists. She even opened her home to aspiring NASA employees who needed a place to stay.&lt;/p&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%2F0gzgnqzmxctzion5m5tt.jpeg" 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%2F0gzgnqzmxctzion5m5tt.jpeg" alt="Katherine Johnson, a Black woman with her hair styled short and wearing a striped sweater and glasses with floral embellishments, in black and white, looking at the camera" width="800" height="1208"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: nasa.gov&lt;/p&gt;

&lt;p&gt;When NACA became NASA, &lt;a href="https://www.nasa.gov/centers-and-facilities/langley/katherine-johnson-biography/" rel="noopener noreferrer"&gt;Katherine Johnson&lt;/a&gt; joined the Space Task Group. Katherine calculated trajectories for America's first manned space flight. She was the first woman in the Flight Research Division to receive author credit on a research report. In 1962, astronaut John Glenn said he would only do the Friendship 7 mission if Katherine hand-checked the IBM computer's trajectory calculations. Katherine also did the calculations that synced the Apollo Project’s Lunar Module with the lunar-orbiting Command and Service Module. She worked on the Space Shuttle and the first satellite launched to study and monitor Earth’s landmasses. She authored or coauthored 26 research reports. In 2015, she was awarded the Presidential Medal of Freedom.&lt;/p&gt;

&lt;h2&gt;
  
  
  Molly Holzschlag
&lt;/h2&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%2Fqov1s1ip3n3xx5b10war.jpeg" 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%2Fqov1s1ip3n3xx5b10war.jpeg" alt="Molly Holzschlag, her short gray hair curly and wild, yelling at the camera" width="775" height="500"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: tucsonsentinel.com &lt;/p&gt;

&lt;p&gt;In 2023, I watched accessibility advocates mourn Molly Holzschlag, the &lt;a href="https://www.tucsonsentinel.com/local/report/090523_molly_holzschlag/tucsons-molly-holzschlag-known-as-the-fairy-godmother-web-dead-60/" rel="noopener noreferrer"&gt;"Fairy Godmother of the Web"&lt;/a&gt;. A contemporary of Sir Tim Berners Lee, she wrote one of the first blogs, earning her the handle &lt;code&gt;mollydotcom&lt;/code&gt;. She was the leader of the Web Standards Group which pushed Microsoft, Opera and Netscape to adopt web standards. After she strong-armed &lt;a href="https://knowbility.org/blog/2022/open-web-pioneer-molly-holzschlag-to-keynote-accessu" rel="noopener noreferrer"&gt;Bill Gates into including CSS in the next Internet Explorer release&lt;/a&gt; he called her &lt;a href="https://toddl.dev/posts/dear-molly/" rel="noopener noreferrer"&gt;"the annoying web standards girl"&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In other words, during the browser wars, she was the person making sure the browsers agreed on enough standards to make the same website code work on all of them. Many of the standards were also aimed at making screen readers actually usable. Her motivation? The world wide web should be usable for everyone.&lt;/p&gt;

&lt;p&gt;Molly died at the age of 60. In her last few years, &lt;a href="https://www.garethjmsaunders.co.uk/2024/09/05/molly-holzschlag-the-fairy-godmother-of-the-web/" rel="noopener noreferrer"&gt;her husband died&lt;/a&gt;, and she grew very ill. She was forced to raise money for her medical bills including &lt;a href="https://news.ycombinator.com/item?id=37401348" rel="noopener noreferrer"&gt;selling off&lt;/a&gt; her eponymous domain &lt;code&gt;molly.com&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Alan Emtage
&lt;/h2&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%2Fhvmhq28ctje11vrmoz95.jpeg" 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%2Fhvmhq28ctje11vrmoz95.jpeg" alt="Alan Emtage, a Black man smiling with short hair and beard, in black and white with water behind him" width="257" height="246"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: internethalloffame.org&lt;/p&gt;

&lt;p&gt;&lt;a href="https://www.internethalloffame.org/inductee/alan-emtage/" rel="noopener noreferrer"&gt;Alan Emtage&lt;/a&gt;, &lt;a href="https://www.lgbtqnation.com/2023/02/the-search-engines-daddy-is-a-black-gay-man/" rel="noopener noreferrer"&gt;a gay Black man from Barbados&lt;/a&gt;, wrote the first search engine, &lt;a href="https://daily.jstor.org/alan-emtage-first-internet-search-engine/" rel="noopener noreferrer"&gt;Archie&lt;/a&gt;. In 1989, working on a mainframe computer meant waiting hours for a dot matrix computer to print out what you needed.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"Rather than spending my time logging on to FTP sites and trying to figure out what was on them, I wrote some computer scripts that would do the same thing, and much faster too." - Alan Emtage&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alan co-founded the world's first Internet startup. As a founding member of the Internet Society, he headed up several groups in &lt;a href="https://www.caribbean-beat.com/issue-122/alan-emtage-codefather" rel="noopener noreferrer"&gt;the Internet Engineering Task Force alongside Sir Tim Berners Lee&lt;/a&gt;. One of those groups standardized Uniform Resource Locators (URLs).&lt;/p&gt;

&lt;p&gt;Alan is a partner at Mediapolis, Inc., the award-winning web development company that operates LGBTQ+ sites under ‘The Datalounge Network". These days, Mediapolis also helps new startups.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mary Ann Horton
&lt;/h2&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%2F82j7tb3914j289v5jdsr.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%2F82j7tb3914j289v5jdsr.png" alt="Mary Ann Horton, her hair styled long with bangs and wearing a colorful shirt, standing next to a screen with an email with an attachment projected on it. Captioned " width="796" height="757"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: maryannhorton.com&lt;/p&gt;

&lt;p&gt;If you've ever used &lt;a href="https://www.geeksforgeeks.org/macros-in-vi-editor/" rel="noopener noreferrer"&gt;macros&lt;/a&gt; or function keys in a vi editor, you have &lt;a href="https://maryannhorton.com/mary-ann-horton/a-career-in-computing/career/" rel="noopener noreferrer"&gt;Mary Ann Horton&lt;/a&gt;, a trans woman, to thank. If you've only used other text editors, you can thank her every time the editor shows you an error in a formatted file. Linux/Unix users and developers owe her a lot. Among other things, she developed &lt;a href="https://maryannhorton.com/mary-ann-horton/a-career-in-computing/email-attachments/" rel="noopener noreferrer"&gt;uuencode/uudecode&lt;/a&gt; which enabled email attachments beyond text files.&lt;/p&gt;

&lt;p&gt;She was also integral to Usenet, one of the first social media networks. As part of the &lt;a href="https://maryannhorton.com/mary-ann-horton/a-career-in-computing/internet-history/" rel="noopener noreferrer"&gt;Backbone Cabal&lt;/a&gt;, she set up spam filtering and started content moderation.&lt;/p&gt;

&lt;p&gt;After college, she started working for Bell Labs. She developed email, Usenet, and internet gateways for AT&amp;amp;T.&lt;/p&gt;

&lt;p&gt;As if all that wasn't enough, she also advocated for &lt;a href="https://maryannhorton.com/mary-ann-horton/transgender-advocacy/" rel="noopener noreferrer"&gt;transgender employment rights&lt;/a&gt; while &lt;a href="https://www.maryannhorton.com/firstday.html" rel="noopener noreferrer"&gt;exploring her own gender identity&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Roy Clay Sr.
&lt;/h2&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%2Fiv0ra7i5xdfzneivy4pj.jpeg" 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%2Fiv0ra7i5xdfzneivy4pj.jpeg" alt="Roy Clay Sr. is in the center, sitting on his desk in his downtown Palo Alto office. 2 of his employees lean on his desk beside him. 2 more sit in chairs in front of his desk. They are all Black. 4 of them are in suits. 1 is a woman in a striped dress." width="600" height="606"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: Roy Clay Sr. family via usatoday.com&lt;/p&gt;

&lt;p&gt;Roy Clay Sr. was one of the first Black men to attend Saint Louis University. After school, he was turned away from an interview because of his race. He spent &lt;a href="https://www.usatoday.com/story/money/2024/09/25/roy-clay-sr-dies/75366211007/" rel="noopener noreferrer"&gt;five years applying&lt;/a&gt; to that company. In 1956, they hired him as a computer programmer. In 1958, he moved to California to write radiation tracking software to map the aftermath of a nuclear explosion. In 1965, David Packard, co-founder of Hewlett-Packard (HP), hired him to head the software devision for their new computers. Roy was director of the team that built the HP 2116A minicomputer, HP's first computer. With his teams, he emphasized results over set work hours. He invented flex time.&lt;/p&gt;

&lt;p&gt;In 1971, Roy left HP to found a consulting firm. The venture capital firm Kleiner Perkins relied on his recommendations, like Intel and Compaq.&lt;/p&gt;

&lt;p&gt;In 1973, he was the first Black person to serve on the Palo Alto, California City Council. At one point, he served as the city’s vice-mayor.&lt;/p&gt;

&lt;p&gt;In 1977, he got wind of new electronic safety requirements. Roy founded ROD-L Electronics, the first company to produce testing equipment that certified products met those requirements. The company was located in East Palo Alto. He hired community residents, who were predominantly Black, earning him the moniker the &lt;a href="https://ontechstreet.com/2015/02/roy-clay-sr-godfather-silicon-valley/" rel="noopener noreferrer"&gt;Godfather of Silicon Valley&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  Edith Windsor
&lt;/h2&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%2Fpz2k5l4hrxbypaoufq97.jpg" 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%2Fpz2k5l4hrxbypaoufq97.jpg" alt="Edith Windsor, in a suit and pearls, confidently staring down the camera" width="800" height="633"&gt;&lt;/a&gt;&lt;br&gt;
Photo credit: nyhistory.org&lt;/p&gt;

&lt;p&gt;You may know Edith "Edie" Windsor as the LGBTQ+ rights activist who sued the U.S. government. Her Supreme Court case, &lt;em&gt;United States v. Windsor&lt;/em&gt;, &lt;a href="https://www.nyhistory.org/exhibitions/edie-windsor-champion-for-marriage-equality" rel="noopener noreferrer"&gt;established a precedent for marriage equality&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;You may not know that Edie, the daughter of Jewish immigrants, was a talented and prolific programmer. While she earned her second degree, Edie worked on nuclear steam supply power systems for the Atomic Energy Commission as &lt;a href="https://jewishhome.org/honorees/edith-windsor/" rel="noopener noreferrer"&gt;a programmer at Combustion Engineering&lt;/a&gt;. This was in the 1950s, so she worked on one of the country's only &lt;a href="https://en.wikipedia.org/wiki/UNIVAC" rel="noopener noreferrer"&gt;UNIVACs&lt;/a&gt;. At the time, it was illegal for her to be homosexual and to wear men's clothes. Terrified, she wore &lt;a href="https://jwa.org/encyclopedia/article/windsor-edie" rel="noopener noreferrer"&gt;"crinolines and a marvelous dress to meet the FBI”&lt;/a&gt; to get clearance to work on the project.&lt;/p&gt;

&lt;p&gt;Edie began her career at IBM as a mainframe programmer. She was there for &lt;a href="https://anitab.org/profile/remembering-edith-windsor-tech-pioneer-equality-advocate/" rel="noopener noreferrer"&gt;16 years&lt;/a&gt;, designing systems architecture and language processors. She eventually reached the company's highest technical rank, Senior Systems Programmer. Known for her "top debugging skills," she owned the first IBM-PC shipped to New York City.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"I could read code until it wrapped around the room and back again. A guy I was working with said, ‘give this woman a roll of toilet paper, she can do anything.'” - Edie Windsor&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;After she left IBM in 1975, she founded a consulting firm called PC Classics. Among other things, they worked to set up computer systems for LGBTQ+ advocacy groups. Lesbians Who Tech have &lt;a href="https://lwtsquad.com/codingscholarship/" rel="noopener noreferrer"&gt;a scholarship for queer women and non-binary techies&lt;/a&gt; in her honor. &lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;The idea that any one race, gender, class, or sexuality is more suited to a technical role is patently false. DEI and accessibility efforts seek to level the playing field, but are slow and currently under attack. We need people to lean into their empathy now.&lt;/p&gt;

&lt;p&gt;Don't know where to start? I wrote &lt;a href="https://dev.to/abbeyperini/8-ways-to-support-women-developers-55lf"&gt;8 Ways to Support Women Developers&lt;/a&gt; in such a way that you can swap out the word "woman" for pretty much any other identifier.&lt;/p&gt;

</description>
      <category>wecoded</category>
      <category>career</category>
      <category>dei</category>
      <category>devchallenge</category>
    </item>
    <item>
      <title>10 Tips from 10 Tech Conferences in 2024</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Fri, 29 Nov 2024 16:21:42 +0000</pubDate>
      <link>https://dev.to/abbeyperini/10-tips-from-10-tech-conferences-in-2024-ik</link>
      <guid>https://dev.to/abbeyperini/10-tips-from-10-tech-conferences-in-2024-ik</guid>
      <description>&lt;p&gt;I attended 10 conferences this year, spoke at 6 of them, and I'm not in DevRel. Here are are my top 10 tips - one for each conference!&lt;/p&gt;

&lt;h2&gt;
  
  
  1. The Hallway Track
&lt;/h2&gt;

&lt;p&gt;Leave time in your schedule to talk with people, visit booths, and decompress. Your brain can only absorb so much technical information at once, and networking is important too.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. No Two Conferences Are the Same
&lt;/h2&gt;

&lt;p&gt;Every conference organizer prioritizes different things. Not every conference is going to be your cup of tea, and that's fine.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. Support Your Local Organizers
&lt;/h2&gt;

&lt;p&gt;Community events are struggling. Go to events. Share you're going. Bring friends. Volunteer. Talk to your company about sponsoring.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. It's a Small World
&lt;/h2&gt;

&lt;p&gt;Be kind, because you never know who knows who. There are multiple people who I saw at four or more conferences this year. This also means speaking and socializing at conferences can lead to speaking opportunities!&lt;/p&gt;

&lt;h2&gt;
  
  
  5. Wear What Makes You Feel Comfortable
&lt;/h2&gt;

&lt;p&gt;Be you. If you're more fabulous than everyone else, someone's going to compliment you. Easy networking.&lt;/p&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%2F7bwqbaoz4mb3r88lr8r0.jpg" 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%2F7bwqbaoz4mb3r88lr8r0.jpg" alt="Two praying mantises looking fabulous" width="697" height="500"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  6. Plan an Easy Way to Connect
&lt;/h2&gt;

&lt;p&gt;I make stickers. You can do business cards or even a QR code. The networking continues after the conference is over.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. Have We Met Before?
&lt;/h2&gt;

&lt;p&gt;Especially as a speaker, you talk to dozens of strangers at a single conference. Don't expect people to remember you, and you don't have to be embarrassed if you don't remember them. If you haven't interacted with someone since the last conference you saw them at, reintroduce yourself and mention the last time you met.&lt;/p&gt;

&lt;h2&gt;
  
  
  8. Travel is Hard
&lt;/h2&gt;

&lt;p&gt;Even if you're practiced and enjoy it, you're always in danger of dehydration. The best conference provides water and snacks, but plan like they won't.&lt;/p&gt;

&lt;h2&gt;
  
  
  9. Read the Emails
&lt;/h2&gt;

&lt;p&gt;Speaking of planning, at least read the schedule before you show up. At some conferences, after-hours events fill up before the conference starts.&lt;/p&gt;

&lt;p&gt;Conference organizers will love you if you read the emails. This is also how you find out if there are days when you are encouraged to wear costumes, and costumes are a surprisingly good networking tool!&lt;/p&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%2Fd3hblsne5x3v9bv3xafw.jpg" 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%2Fd3hblsne5x3v9bv3xafw.jpg" alt="Abbey and Kayla dressed as Gandalf and Saruman at RenderATL 2024" width="800" height="1066"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  10. Shoot Your Speaking Shot
&lt;/h2&gt;

&lt;p&gt;When I was peer pressured into submitting talk ideas to tech conferences' call for proposals (CFPs), I did not expect so many to get get accepted. Now, I pass that pressure on to you. Check out &lt;a href="https://confs.tech/cfp" rel="noopener noreferrer"&gt;confs.tech/cfp&lt;/a&gt; to find some CFPs and submit! If you have no idea where to start, read &lt;a href="https://dev.to/abbeyperini/so-you-need-to-give-a-talk-30b4"&gt;So You Need to Give a Talk...&lt;/a&gt;!&lt;/p&gt;

</description>
      <category>career</category>
      <category>programming</category>
      <category>developer</category>
      <category>networking</category>
    </item>
    <item>
      <title>Dark Mode Toggle in HTML Web Components</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Tue, 22 Oct 2024 20:50:35 +0000</pubDate>
      <link>https://dev.to/abbeyperini/dark-mode-toggle-using-html-web-components-h0g</link>
      <guid>https://dev.to/abbeyperini/dark-mode-toggle-using-html-web-components-h0g</guid>
      <description>&lt;p&gt;When designing my digital garden, I knew I wanted a cute dark mode toggle. Once I had drawn my SVG, I started building a web component that had all the same functionality as my &lt;a href="https://dev.to/abbeyperini/toggle-dark-mode-in-react-28c9"&gt;dark mode toggle in React&lt;/a&gt;. This includes everything I learned while &lt;a href="https://dev.to/abbeyperini/an-accessible-dark-mode-toggle-in-react-aop"&gt;accessibility auditing my site&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;In addition to changing the theme, the toggle needs to take the user's &lt;a href="https://dev.to/abbeyperini/dark-mode-toggle-and-prefers-color-scheme-4f3m"&gt;&lt;code&gt;prefers-color-scheme&lt;/code&gt; selection&lt;/a&gt; into account and persist the user's preference across reloads. For accessibility, the toggle's screen reader announcement must make sense (e.g. "dark mode toggle on"). Since I want to display an SVG instead of a checkbox with text, I'll have to add focus and hover styling and a label that shows up on hover.&lt;/p&gt;

&lt;h2&gt;
  
  
  Toggle Web Component
&lt;/h2&gt;

&lt;p&gt;First, I need a Toggle class that creates an HTML element. Using the &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Web_components/Using_custom_elements" rel="noopener noreferrer"&gt;Custom Element API&lt;/a&gt;, I'll define &lt;code&gt;&amp;lt;toggle-component&amp;gt;&lt;/code&gt; using this class.&lt;/p&gt;

&lt;p&gt;Using the class's constructor, I set the &lt;code&gt;innerHTML&lt;/code&gt; of &lt;code&gt;&amp;lt;toggle-component&amp;gt;&lt;/code&gt; to a &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; with an &lt;code&gt;&amp;lt;input type="checkbox"&amp;gt;&lt;/code&gt; and SVG as children. The label has a &lt;code&gt;title&lt;/code&gt; attribute, giving our toggle a visible "dark mode toggle" label if a user hovers over it or the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt;. Because the checkbox is a child of the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt;, interacting with the &lt;code&gt;&amp;lt;label&amp;gt;&lt;/code&gt; is the same thing as interacting with the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; and vice versa. The &lt;code&gt;title&lt;/code&gt; attribute also makes sure screen readers announce "dark mode toggle" when a user interacts with this component.&lt;/p&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%2Fcmbpjaunp3pyi2p4mb02.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%2Fcmbpjaunp3pyi2p4mb02.png" alt="A white daisy on a green background with a label that says " width="454" height="249"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Once the HTML is in place, I add a &lt;code&gt;connectedCallback&lt;/code&gt; function to the class. This part of the custom elements API defines functions for use within the component and executes code when the component is inserted into the DOM.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// /components/toggle.js&lt;/span&gt;

&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;Toggle&lt;/span&gt; &lt;span class="kd"&gt;extends&lt;/span&gt; &lt;span class="nc"&gt;HTMLElement&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;super&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;innerHTML&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;`
      &amp;lt;label title="dark mode toggle"&amp;gt;
        &amp;lt;input type="checkbox" id="theme-toggle" class="theme-switch" /&amp;gt;
        &amp;lt;svg id="daisy"&amp;gt;{SVG code removed for brevity}&amp;lt;/svg&amp;gt;
      &amp;lt;/label&amp;gt;
    `&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;class&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;toggle-component&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;connectedCallback&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;switchTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;e&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;target&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;

      &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;   
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;themeName&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;themeName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;documentElement&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;setAttribute&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;data-theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;themeName&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;setCheckBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toggleSwitch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;toggleSwitch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;checked&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;?&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt; &lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="kd"&gt;function&lt;/span&gt; &lt;span class="nf"&gt;keepTheme&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;toggleSwitch&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;querySelector&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;#theme-toggle&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nx"&gt;toggleSwitch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;change&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;switchTheme&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;localStorage&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;getItem&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;theme&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="nf"&gt;setCheckBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toggleSwitch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;theme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;prefersLightTheme&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;matchMedia&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;(prefers-color-scheme: light)&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;prefersLightTheme&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;matches&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;light&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
      &lt;span class="p"&gt;};&lt;/span&gt;

      &lt;span class="nf"&gt;setTheme&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
      &lt;span class="nf"&gt;setCheckBox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;toggleSwitch&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dark&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;

    &lt;span class="nb"&gt;document&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;DOMContentLoaded&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;keepTheme&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;    
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nx"&gt;customElements&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;define&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;toggle-component&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;Toggle&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Because &lt;code&gt;&amp;lt;toggle-component&amp;gt;&lt;/code&gt; is inserted into the DOM before the page loads, the only code that executes immediately adds an event listener. The event listener calls &lt;code&gt;keepTheme&lt;/code&gt; as soon as the page has loaded. First, &lt;code&gt;keepTheme&lt;/code&gt; adds an event listener to the &lt;code&gt;&amp;lt;input&amp;gt;&lt;/code&gt; that calls &lt;code&gt;switchTheme&lt;/code&gt; when a user interacts with it. &lt;code&gt;switchTheme&lt;/code&gt; passes 'dark' to &lt;code&gt;setTheme&lt;/code&gt; if the checkbox is checked and 'light' if it is not. The string passed to &lt;code&gt;setTheme&lt;/code&gt; is set as the CSS theme and saved in &lt;code&gt;localStorage&lt;/code&gt; which will persist across reloads.&lt;/p&gt;

&lt;p&gt;The rest of &lt;code&gt;keepTheme&lt;/code&gt; is dedicated to choosing the right theme on load. First, it checks &lt;a href="https://developer.mozilla.org/en-US/docs/Web/API/Window/localStorage" rel="noopener noreferrer"&gt;&lt;code&gt;localStorage&lt;/code&gt;&lt;/a&gt; to see if the user's preference is already set. Next, it checks if &lt;code&gt;prefers-color-scheme&lt;/code&gt; is set to 'light'. Finally, it defaults to dark mode. For both dark and light mode, I call &lt;code&gt;setTheme.&lt;/code&gt; For dark mode, I also call &lt;code&gt;setCheckbox&lt;/code&gt;. The checkbox mounts in an unchecked state. A screen reader will announce "dark mode" and whether the checkbox is checked. To get an announcement like "dark mode toggle checked" or "dark mode toggle on", I have to programmatically check the checkbox when I set the theme to 'dark' on load.&lt;/p&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%2Fvuiklivalqghn8n3bs4e.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%2Fvuiklivalqghn8n3bs4e.png" alt="A screenshot showing the toggle in dark mode focused by a screen reader and the announcement is " width="800" height="300"&gt;&lt;/a&gt;&lt;/p&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%2Fm80jyv3tb04e336l24q0.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%2Fm80jyv3tb04e336l24q0.png" alt="A screenshot showing the toggle in light mode focused by a screen reader and the announcement is " width="800" height="303"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Toggle Styling
&lt;/h2&gt;

&lt;p&gt;I chose to draw a fairly simple design so I could put my SVG code directly in the web component and programmatically change the fill color. This way, the background color of the daisy always matches the theme. Next, I use &lt;code&gt;opacity: 0;&lt;/code&gt; to hide the checkbox and position it in the middle of the image. Finally, I add the hover and focus styles.&lt;/p&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%2Fbolbhhzm79dlyuy4kh6v.gif" 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%2Fbolbhhzm79dlyuy4kh6v.gif" alt="the toggle cycling through all its possible color combinations as it is clicked and focused and the theme switches" width="318" height="248"&gt;&lt;/a&gt;&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight css"&gt;&lt;code&gt;&lt;span class="c"&gt;/* /styles/styles.css */&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"light"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--toggle-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#242D54&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="o"&gt;[&lt;/span&gt;&lt;span class="nt"&gt;data-theme&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;"dark"&lt;/span&gt;&lt;span class="o"&gt;]&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;--toggle-background&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;#282e53&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nf"&gt;#daisy&lt;/span&gt; &lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;var&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;--toggle-background&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.theme-switch&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;position&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;relative&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;bottom&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;30px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;left&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;55px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;width&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;height&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;1em&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;opacity&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;0&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.theme-switch&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;#daisy&lt;/span&gt; &lt;span class="nt"&gt;path&lt;/span&gt;&lt;span class="o"&gt;,&lt;/span&gt;
&lt;span class="nc"&gt;.theme-switch&lt;/span&gt;&lt;span class="nd"&gt;:hover&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;#daisy&lt;/span&gt; &lt;span class="nt"&gt;path&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="py"&gt;fill&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;

&lt;span class="nc"&gt;.theme-switch&lt;/span&gt;&lt;span class="nd"&gt;:focus&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="nf"&gt;#daisy&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nl"&gt;outline&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;3px&lt;/span&gt; &lt;span class="nb"&gt;solid&lt;/span&gt; &lt;span class="no"&gt;white&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
  &lt;span class="nl"&gt;outline-offset&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="m"&gt;5px&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Using the Toggle Web Component
&lt;/h2&gt;

&lt;p&gt;All I need to do is import my stylesheet and component script in the &lt;code&gt;&amp;lt;head&amp;gt;&lt;/code&gt; of an HTML page. Then I can call &lt;code&gt;&amp;lt;toggle-component&amp;gt;&lt;/code&gt; anywhere in the page.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight html"&gt;&lt;code&gt;&lt;span class="c"&gt;&amp;lt;!-- index.html --&amp;gt;&lt;/span&gt;

&lt;span class="nt"&gt;&amp;lt;html&lt;/span&gt; &lt;span class="na"&gt;lang=&lt;/span&gt;&lt;span class="s"&gt;"es"&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;head&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;link&lt;/span&gt; &lt;span class="na"&gt;rel=&lt;/span&gt;&lt;span class="s"&gt;'stylesheet'&lt;/span&gt; &lt;span class="na"&gt;href=&lt;/span&gt;&lt;span class="s"&gt;'../styles/styles.css'&lt;/span&gt;&lt;span class="nt"&gt;/&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;script &lt;/span&gt;&lt;span class="na"&gt;src=&lt;/span&gt;&lt;span class="s"&gt;"../components/toggle.js"&lt;/span&gt; &lt;span class="na"&gt;type=&lt;/span&gt;&lt;span class="s"&gt;"text/javascript"&lt;/span&gt; &lt;span class="na"&gt;defer&lt;/span&gt;&lt;span class="nt"&gt;&amp;gt;&amp;lt;/script&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/head&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;body&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;header&amp;gt;&lt;/span&gt;
      &lt;span class="nt"&gt;&amp;lt;toggle-component&amp;gt;&amp;lt;/toggle-component&amp;gt;&lt;/span&gt;
    &lt;span class="nt"&gt;&amp;lt;/header&amp;gt;&lt;/span&gt;
  &lt;span class="nt"&gt;&amp;lt;/body&amp;gt;&lt;/span&gt;
&lt;span class="nt"&gt;&amp;lt;/html&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&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%2Fl37nfe5vi6f3ggitnus5.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%2Fl37nfe5vi6f3ggitnus5.png" alt="A screenshot of Abbey's digital garden in dark mode" width="800" height="385"&gt;&lt;/a&gt;&lt;/p&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%2Fj8i4nn4bv863b7tw92og.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%2Fj8i4nn4bv863b7tw92og.png" alt="A screenshot of Abbey's digital garden in light mode" width="800" height="376"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Conclusion
&lt;/h2&gt;

&lt;p&gt;I had fun getting my dark mode toggle to work just as well in a web component as it does in React. You can see this live in my &lt;a href="https://abbeyperini.com" rel="noopener noreferrer"&gt;digital garden&lt;/a&gt; and the full code in the &lt;a href="https://github.com/abbeyperini/digital-garden" rel="noopener noreferrer"&gt;GitHub repo&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>css</category>
      <category>html</category>
      <category>tutorial</category>
      <category>webdev</category>
    </item>
    <item>
      <title>So You Need to Give a Talk...</title>
      <dc:creator>Abbey Perini</dc:creator>
      <pubDate>Mon, 30 Sep 2024 18:45:15 +0000</pubDate>
      <link>https://dev.to/abbeyperini/so-you-need-to-give-a-talk-30b4</link>
      <guid>https://dev.to/abbeyperini/so-you-need-to-give-a-talk-30b4</guid>
      <description>&lt;p&gt;Take it from someone who barely passed their college public speaking course - you can definitely give a tech talk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Pick an Idea
&lt;/h2&gt;

&lt;p&gt;Everyone has a unique perspective or experience that is worth sharing. Every time you explain something to a coworker, solve a unique problem at work, dive down a research rabbit hole, or solve a hard problem, that's an opportunity to write a talk.&lt;/p&gt;

&lt;h2&gt;
  
  
  Marketing
&lt;/h2&gt;

&lt;p&gt;Whether it's a meetup or conference call for proposals (CFP), you'll need a title and an abstract. If you can come up with a witty title, great. Otherwise, a short, descriptive title is perfectly fine.&lt;/p&gt;

&lt;p&gt;Think about the abstracts you've read that made you want to attend a talk. Don't list out every point you're going to make. Do convey what attendees should expect to learn during your talk.&lt;/p&gt;

&lt;p&gt;Many CFPs ask for supporting materials or more explanation. This is the place to list out all of the points you want to cover and show you understand the topic. All of my talks start as blogs, so I usually use a link to the blog. &lt;/p&gt;

&lt;p&gt;Save all of this information after you've typed it out. You can and should submit the same talk to multiple events. If the talk never gets picked up, it can always be turned into written or video content. If it gets picked up multiple times, you'll be able to improve upon it iteratively.&lt;/p&gt;

&lt;p&gt;If you're mainly motivated by deadlines, you can wait for your talk proposal to get accepted before working on it more. If you're excited about the idea, you can go ahead and start your outline.&lt;/p&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%2F0m5iw4pyg97trhjbfs9q.jpg" 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%2F0m5iw4pyg97trhjbfs9q.jpg" alt="Darth Vader saying " width="600" height="337"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Outline
&lt;/h2&gt;

&lt;p&gt;Just like in the more explanation part of the CFP, write down the main points you want to convey during your talk. Once you're done, read back through your points from the prospective of an attendee. Do any of the points require prerequisite knowledge or more explanation?&lt;/p&gt;

&lt;p&gt;The best talks have a flow that is similar to a story - a beginning, middle, and end. If your beginning involves introducing yourself, make it relevant. For example, why you're an expert on this subject or how you got the idea for the talk. If you're talking about a problem and a solution, the beginning is the time to outline the problem you're going to solve. The middle sets up all the necessary context for the end. The end is your conclusion, recommendation, or solution.&lt;/p&gt;

&lt;h2&gt;
  
  
  Slides
&lt;/h2&gt;

&lt;p&gt;Find a slides program you are comfortable using. These days, your slides double as notes for attendees to use after your talk, so don't forget to include ways to connect with you and a QR code with a link to your slides. If you don't want to use slides, structure what you will be showing on the screen so that you can still give a copy to the audience. For example, a code repository with comments in the code. I've even heard of making a copy of your terminal history and publishing a link to it if your talk is entirely in the command line.&lt;/p&gt;

&lt;p&gt;Put your outline in your slides (or code repository or terminal history). Include links to relevant &lt;a href="https://codepen.io/your-work" rel="noopener noreferrer"&gt;codepens&lt;/a&gt;, code repositories, documentation and resources. Include helpful visual aids with captions and/or memes with alt text.&lt;/p&gt;

&lt;p&gt;An outline should be just enough words to get your point across. Don't write down the entire script of your talk. You don't need a slide for every sentence you're going to say. Use link formatting to turn text into links rather than bare URLs. Whatever you are showing on the screen should support what you are saying, not distract from it.&lt;/p&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%2F5jzfim2f18rk09csf38z.jpg" 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%2F5jzfim2f18rk09csf38z.jpg" alt="Someone presenting saying " width="420" height="294"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Practice
&lt;/h2&gt;

&lt;p&gt;Practice includes rehearsing a specific talk you need to give and giving talks in front of audiences. You'll see that the people with lots of public speaking experience don't have to rehearse specific talks as much. Until you have made a mistake and recovered in front of an audience, it's a good idea to know your talk really well before you get on stage. If you rehearse, when something goes wrong or throws you off, it's much easier to remember what you were going to say and get back on track.&lt;/p&gt;

&lt;p&gt;Experienced speakers will tell you speaking in front of an audience is the only way to get comfortable speaking in front of an audience. So utilize opportunities to give demos and presentations at work and in school. Ask your friends and family to listen to your talk even if they don't understand the topic.&lt;/p&gt;

&lt;p&gt;If there is dedicated time for questions or you'll allow attendees to ask you questions after your talk, you need to be familiar with the topic beyond what's on your slides. If you want to use a clicker, play a video, use a prop, or live code, rehearse with these things.&lt;/p&gt;

&lt;p&gt;Unless you have confirmed beforehand with the event that you will have reliable internet and multiple screens, practice your presentation without internet on a single screen. &lt;/p&gt;

&lt;p&gt;You can always use paper notes, but I prefer to make my slides my notes. If you read your notes like a script, it will sound like you're reading a script. As I rehearse, I'll add slides with points I keep forgetting to say and remove slides I find I don't need.&lt;/p&gt;

&lt;p&gt;If you're live coding, set up examples that require the least amount of typing. This usually means commenting and uncommenting chunks of code or only running simple commands.&lt;/p&gt;

&lt;p&gt;If you're planning on doing a demo (especially if it's your company's product), record yourself demoing before the talk. Something always goes wrong with a demo, so you may need to play the recording instead.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Big Day
&lt;/h2&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%2Fkjgp8gz3l8xg83cccskp.jpeg" 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%2Fkjgp8gz3l8xg83cccskp.jpeg" alt="A cartoon of a chicken saying " width="400" height="400"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;As a speaker, you'll find every other speaker understands that your goal is to minimize your anticipatory anxiety until you've given your talk. Prepare for this. In the weeks leading up to the talk, find a few anxiety reduction techniques that work for you and use them on the big day. I find it extremely helpful to ask a friend to set aside 30 minutes before my talk just to hang out and talk with me. Don't pick the day of your talk to do things that spike your anxiety. Do remember to eat regular meals and drink plenty of water. Charge your devices and bring adaptors for display cords. &lt;a href="https://www.amazon.com/gp/product/B08VHB4VGK" rel="noopener noreferrer"&gt;This puck&lt;/a&gt; has saved many butts, including mine. &lt;/p&gt;

&lt;p&gt;Attend other talks and hang out in the speaker room. This way, you'll find out if other speakers are running into problems with the setup and have already found solutions. Get to your speaking location early so you have time to do a tech check.&lt;/p&gt;

&lt;p&gt;If you don't have friends attending the event, befriend other speakers and attendees. If all else fails and you start panicking, a friendly face makes a big difference.&lt;/p&gt;

&lt;p&gt;Most importantly, remember the audience wants to be interested in your talk and thus wants you to succeed.&lt;/p&gt;

</description>
      <category>career</category>
      <category>techtalks</category>
      <category>programming</category>
      <category>devrel</category>
    </item>
  </channel>
</rss>
