<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:media="http://search.yahoo.com/mrss/"><channel><title><![CDATA[Stephen Mayeux]]></title><description><![CDATA[Software Engineer and Lifelong Learner]]></description><link>https://www.stephenmayeux.com/</link><image><url>https://www.stephenmayeux.com/favicon.png</url><title>Stephen Mayeux</title><link>https://www.stephenmayeux.com/</link></image><generator>Ghost 4.48</generator><lastBuildDate>Mon, 03 Nov 2025 11:11:05 GMT</lastBuildDate><atom:link href="https://www.stephenmayeux.com/rss/" rel="self" type="application/rss+xml"/><ttl>60</ttl><item><title><![CDATA[Jerome, Go Around]]></title><description><![CDATA[This is a short story based on the near-hit that occurred at KAUS on February 4, 2023. ]]></description><link>https://www.stephenmayeux.com/jerome-go-around/</link><guid isPermaLink="false">67b8c89a76c395246aa09ce9</guid><category><![CDATA[Fiction]]></category><category><![CDATA[Writing]]></category><category><![CDATA[Personal]]></category><dc:creator><![CDATA[Stephen Mayeux]]></dc:creator><pubDate>Fri, 21 Feb 2025 22:06:29 GMT</pubDate><media:content url="https://storage.stephenmayeux.com/2025/02/southwest-kaus.webp" medium="image"/><content:encoded><![CDATA[<div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-text">On February 4, 2023, a <a href="https://www.nytimes.com/2023/10/11/business/air-traffic-control-austin-airport-fedex-southwest.html?smid=url-share">near-hit</a> occurred at my home airport in Austin, TX. A Southwest 737 carrying 128 passengers and crew came within 100 feet of a FedEx 767 which had a crew of three members. Fortunately the FedEx pilots executed a go-around, sparing the lives of everybody involved.<br><br>In aviation, there is almost never a single cause for accidents such as this one. All the holes of the Swiss cheese have to line up, and when they do and tragedy befalls us, it becomes so easy to be swept up into the media frenzy and blame a single person or company.<br><br>The tower controller at KAUS that day was unfairly identified by the press and scrutinized publicly. This short story, a work of fiction, is dedicated to him.&#xA0;</div></div><img src="https://storage.stephenmayeux.com/2025/02/southwest-kaus.webp" alt="Jerome, Go Around"><p>	The foot was still in the brown leather shoe, and it belonged to the passenger who was still strapped to his seat about a hundred yards away. His body was burned beyond all recognition, charred from the heat of the explosion so fiercely hot that it melted the aluminum airframe of United flight 255.</p><p>	Black, acrid smoke filled the air around the crash site, choking the few disoriented survivors who were crying in agony. The luggage of all 168 passengers and crew aboard had been ripped open, and their contents were strewn about a half-mile radius around the airport, dotting the runway and the grassy fields , transforming it into a war torn shanty town.</p><p>	A woman with white hair limped away from the horrific accident until she could no longer feel the heat from the fires, and splashed her face clean with a bottle of water she had kept in her fanny pack. There was a metal shard sticking out of her leg, and she was bleeding out. The woman looked down and touched the shrapnel, wincing in pain.</p><p>	&quot;Why did you let this happen?&quot; she asked. &quot;This is all your fault!&quot;</p><hr><p>	They warned Jerome at the start of his career about the dreams this job would give him. He shrugged it off at first, but when his nightmares kept waking him up on some nights before the 12-hour shifts at the Austin Bergstrom International Airport control tower, he thought his coworkers had transferred some curse to him. It was a terrible, bloody vision that was in the back of his mind while he sat at his terminal, directing commercial airline traffic carrying tens of thousands of souls every day in fast-moving tin tubes. A solemn reminder that demanded him not to fuck it up.</p><p>	In the four years Jerome was assigned to the Austin control tower, he got in hot water with his supervisor only three times, but almost nothing that caused harm or serious operational delays. In the first week after his probationary period had ended, he directed a Delta plane to a taxiway that was closed off for repairs, stranding the Airbus 220 for more than an hour with no forward way out. It had to be towed out backwards to the next open taxiway using a specialized vehicle, which happened to get a flat tire on the way to rescue the Delta plane.</p><p>	Last year, towards the end of a particularly stressful day caused by snowstorms in the northeast, Jerome mixed up two very similar sounding call signs on the radio: eight-three-niner-whiskey and eight-three-niner-whiskey-papa, both Piper Cherokees, a single-engine training aircraft, who just happened to be inbound to the airport at the same time. When Jerome instructed niner-whiskey to turn left heading 270 degrees, he had actually intended to give those instructions to niner-whiskey-papa, a verbal slip up that would have put a student pilot and his instructor on a collision course with a private business jet. Fortunately the instructor immediately recognized the hazardous situation that heading would have put them in and broadcasted &#x201C;They&#x2019;re trying to kill us here in Austin!&#x201D; on the approach radio frequency.</p><p>	Mistakes like these happened at every airport in America more regularly than the general public was made aware of, and most of them were never reported on. But every so often, a seriously grave error in judgment by either a pilot or controller, or both, put lives in danger and made the headlines. </p><p>	On a February morning around 6:45, early into his shift, Jerome did something and knew that he had screwed the pooch, but it was not until he was dismissed and sent home early for the day, plopped on the sofa, and saw footage of the near collision that he caused posted on TikTok, that he realized he had utterly and most unequivocally fucked it up.</p><p>	Jerome had cleared a large FedEx cargo aircraft, a Boeing 767, to land on the same runway that a passenger airliner was taking off from, and the two aircraft came within 100 feet of each other before the captain of the FedEx flight executed a go-around. </p><p>	&#x201C;What happened?&#x201D; he asked himself. &#x201C;What the hell did I just do?!&#x201D; </p><p>	About 140 Texans were boarding Southwest flight 708 to escape the brief, frigid winter season that seems to last only a few weeks, and set off on their way to Cabo San Lucas. The weather made a turn for the better overnight: warm, moist Gulf air moved through and thawed the icy remnants of a winter storm, but condensed into a thick fog that blanketed the entire city.</p><p>	&#x201C;Southwest 708, cleared for takeoff without delay, runway 1-8 left&#x201D; Jerome said robotically. &#x201C;There&#x2019;s a 767, five mile final, landing same runway.&#x201D;</p><p>	The observation deck from the control tower was only 90 feet high, just barely poking above a thick layer of ground fog. Clear blue skies were visible above, but the entire airport was in the soup. Pilots would have to progressively taxi from point to point, reporting their positions along the way as the controllers were unable to see what was happening on the ground beneath them.</p><p>	&#x201C;Austin tower, this is FedEx 1432. Are we still cleared to land? Where&#x2019;s that departing traffic?&#x201D; Moments later, the FedEx captain called out in a panic, &#x201C;Southwest, abort take off! FedEx is going around!&#x201D;</p><p>	The sound of genuine fear in his voice replayed again and again on the nightly news, while the pundits speculated on what happened and placed blame on everything from overworked ATC personnel to Jerome&#x2019;s character and whether or not he was fit to be working in a control tower in the first place. It was his nightmare coming alive to be shared on every television and mobile phone in America for the next forty-eight hours.</p><hr><p>	After a couple of weeks since Jerome was sent home for clearing a cargo jet to land atop a commercial airliner, he was able to enroll in remedial training to maintain the currency of his ATC license and was eventually reassigned to clearance control at the airport, which was the first controller a pilot would speak to while on the ground, usually to file a flight plan. Although a lot of his colleagues saw this as an obvious demotion, Jerome was given the easiest and least stressful position while he put the pieces of his life back together and the FAA and National Transportation Safety Board conducted their investigation of the incident.</p><p>	Several months after returning to work as a clearance controller, &#xA0;Jerome was scheduled to talk with a few representatives from the NTSB. The agency had already conducted a preliminary analysis of the weather, the events of that morning, and the circumstances that lead to two large jets coming within 100 feet of each other.</p><p>	The interview was scheduled to take place at the Office of Aviation, which was a small, one story building next to the cell phone lot at the Austin Bergstrom airport. The parking lot to the office was nearly empty, except for one other car. Jerome parked next to it and walked inside.</p><p>	There was a single conference room in the building, and it was easy for him to find. There was only one person waiting for him inside the room. Sandra Hugo was a Human Performance Specialist at the NTSB. She investigated aviation accidents and specialized in determining whether human psychophysiogical factors were a contributing cause. She had short, blond hair that was in very tight curls. She seemed so small while she sat at the long, empty table and wrote in her notebook. Jerome walked in. She removed the bifocals that were resting on her nose, and they hung around her neck. </p><p>	She looked up and said, &quot;You&apos;re in the right place. Come in and have a seat.&quot;</p><p>	Jerome walked over to shake hands with Sandra. She was a short woman and the top of her head came up to Jerome&apos;s neck.</p><p>	&quot;This is it?&quot; Jerome asked. &quot;I thought there would be more people today.&quot;</p><p>	&quot;This is it. Only us and the tape recorder.&quot;</p><p>	Jerome took a seat across the table and felt immediately at ease. He was expecting at least four or five guys from the agencies, the type of men with thick mustaches and beer bellies, who might crack a joke about Jerome&apos;s time in the Navy and then follow it up with a quick non-apology, like &quot;Oh, we&apos;re just messing with you, squid. At least you&apos;re not Army.&quot;</p><p>	&quot;I was expecting more of you guys today, but this works, too.&quot;</p><p>	&quot;Expecting more of us? How come?&quot; Sandra asked.</p><p>	&quot;I know what I did back in February, and a good ol&apos; government ass-chewing is usually a group activity.&quot;</p><p>	&quot;That&apos;s not what today is about.&quot; Sandra reassured him. &quot;Let&apos;s just start the darn thing, and get this interview over with. How about that?&quot;</p><p>	&quot;Whatever you want to do.&quot;</p><p>	Sandra stood up to reach across the table and start the recorder. &quot;OK, today&apos;s June the third, 2023. I&apos;m Sandra Hugo with the NTSB,&quot; she started. &quot;I&apos;m here to discuss the incident that occurred on February the fourth with an air traffic tower controller at Austin Bergstrom. Could you please state your name and spell it for me?&quot;</p><p>	&quot;Yes, I&apos;m...&quot; Jerome started. &quot;My name. My name?&quot; He looked up at Sandra, puzzled, unsure of how to answer the question.</p><p>	Sandra stopped the recorder. &#x201C;Everything OK? Do you need some water?&#x201D;</p><p>	&quot;I&apos;m so sorry, but can you tell me my name?&quot; At this point, Jerome was starting to become upset with himself. Forgetting his name at the start of an interview with an aviation specialist who had been investigating his cognitive and physical fitness was not a good start at all. His assuredness that this interview was just a checkbox that needed to be done completely vanquished, and he was worried about what was around the corner.</p><p>	&quot;This is so weird,&quot; Jerome admitted. &quot;But can you help me out? I can&apos;t remember my name.&quot;</p><p>	&quot;I know you can&apos;t remember it. Because I took your name away from you. Just for a short time.&quot;</p><p>	A wave of confusion washed over Jerome. &quot;You took my name away from me?&quot;</p><p>	&quot;I did,&quot; Sandra confessed, &quot;but I promise to give it back to you. I know you must be freaked out right now. One minute, you&apos;re part of a national news story about air traffic safety, sitting in a room talking to a federal investigator. The next minute, you can&apos;t even remember your own name, and this woman you&apos;ve never met before is telling you that &apos;she just took it away&apos;, as if I&#x2019;m asking you to borrow a pen or something like that.&quot;</p><p>	Jerome squirmed in his seat, still unsure what to make of what Sandra was telling him.</p><p>	She continued explaining, &quot;You&apos;re probably freaked out right now that I have your name. As if a part of you is missing. Like how the clouds move in front of the Sun, and your shadow doesn&apos;t completely go away, but gets fainter and lighter. Does that make sense?&quot;</p><p>	&quot;It doesn&apos;t, and I am freaked out. This is the most interesting experience I&apos;ve ever had, ever.&quot;</p><p>	Sandra chuckled loudly. &quot;Interesting? I&#x2019;m telling you that I took your name, and you call that interesting! You don&apos;t really have a knack for words, but that&apos;s OK. Let&apos;s get to the point of why I&apos;m here.&quot;</p><p>	&quot;That&apos;d be nice.&quot;</p><p>	&quot;I&apos;m here to help you remember what happened three months ago, on the morning when FedEx almost landed on top of a Southwest. I&apos;m very good at helping people remember things, and forgetting, too. The name thing was just to show you what I can do, and I promise to give your name back when we&apos;re finished today. Sound good?&quot;</p><p>	&quot;This is pretty far from good, lady, but let&apos;s get the show on the road.&quot;</p><p>	Sandra started the recording again. &quot;I want to talk about the seventy-two hours prior to the incident. How have you been sleeping the past few days?&quot;</p><p>	&quot;Pretty good, I guess.&quot;</p><p>	&quot;How much sleep would you say you need to feel rested?&quot;</p><p>	&quot;About 6 hours.&quot;</p><p>	&quot;And how much sleep do you usually get?&quot;</p><p>	&quot;About 6 to 8 hours,&quot; Jerome answered honestly.</p><p>	&quot;And the night before the incident, how much sleep did you get?&quot;</p><p>	Jerome thought about the question for a moment and then answered, &quot;I don&apos;t recall. It happened a few months ago.&quot;</p><p>	&quot;How much sleep the night before?&quot;</p><p>	Like a gentle wind across a meadow, the memory of the night before slowly came back to Jerome.</p><p>	&quot;I went to bed around midnight that night and woke around 5:30 in the morning,&quot; Jerome answered. &quot;But I wasn&apos;t tired at work or nothing like that. Once I&apos;m up, I&apos;m up.&quot;</p><p>	&quot;You&apos;re a Navy veteran, aren&apos;t you? When did you serve?&quot;</p><p>	&quot;First time was 2002 to 2005, and then again from 2007 to 2009.&quot;</p><p>	&quot;Wow, so you went back for seconds?&quot;</p><p>	&quot;Like a dummy.&quot;</p><p>	&quot;And that&apos;s why you really don&apos;t need 6 hours to sleep, right? The Navy did that to you?&quot;</p><p>	&quot;When you&apos;re out to sea for long stretches like we were, you never really get a chance to be comfortable. Just passed out from exhaustion, up and at it again a few hours later.&quot;</p><p>	Jerome, still unsure what his name was and if Sandra was really an investigator from the NTSB, kept answering her questions for the next hour or so. Sandra wanted to know everything about his health history, medications he was taking, as well as any extenuating circumstances that might have distracted him the morning of the incident. &#xA0;She asked Jerome if he needed to stop and take a break, and he shook his head yes.</p><p>	&quot;One more question before we stop for a break: you ran control out of Chicago, and then Atlanta before coming down to Texas. Why Austin?&quot;</p><p>	&quot;I got divorced a few years back. She moved to Houston, and I wanted to be closer to the kids. I put in a bid to come here, and the closest I could get to them was Austin.&quot;</p><p>	&quot;When was the last time you saw them?&quot;</p><p>	Jerome shook his head and chuckled in disbelief as the memories came back to him. &quot;I saw them last year during Christmas, but I was actually supposed to see the oldest at a poetry slam I was performing at.&quot;</p><p>	&quot;Poetry slam? So you do have a knack for words then.&quot;</p><p>	&quot;Not really. Not really that good. But yeah, my oldest can drive now, and I wanted her to come up to the slam.&quot; Jerome stopped to consider this. &quot;But something last minute happened and she couldn&apos;t make it down here. Probably her mother getting in the way, getting jealous, blah, blah, blah.&quot; </p><p>	He paused and continued, &quot;It took me a while to fall asleep that night because I was upset. But that wasn&#x2019;t the cause of the accident. You&#x2019;re just asking about my kids and stuff, so that&#x2019;s why I&#x2019;m talking about them.&quot;</p><p>	&quot;What would you say was the cause?&quot;</p><p>	&quot;Two things: first thing was an expectation bias that us tower guys have about Southwest pilots. When we clear them to take off, they turn around the corner and go. Always. Southwest never wastes a second. American, usually not. Allegiant Airlines, not a chance in hell with those guys.&quot;</p><p>	Sandra laughed and appreciated the deprecating sense of humor that aviators and veterans always laid out against each other. &quot;What was the other reason? You said there were two.&quot;</p><p>	&quot;Lack of ground radar at Austin. Like I told you and everyone else about this whole thing. It was foggy as hell that morning. So unusual for Austin any time of year really. But if we had any type of ground radar on the field, I would&apos;ve known if Southwest was on the move or not.&quot;</p><p>	He stopped to consider and went on. &quot;But hindsight&apos;s twenty-twenty. I shouldn&apos;t have cleared that Southwest jet when FedEx was so close to the airport. I shouldn&apos;t have tried to squeeze that departure out.&quot;</p><p>	Sandra was writing a note in her book, finished, and then put her pen down.</p><p>	&quot;Well, I think those are all the questions that I have now. And I almost forgot! I never got you to say your name at the beginning when I started the recording. Could you please state your name?&quot;</p><p>	&quot;Jerome Lays. Jerome with a &apos;J&apos;, and Lays like the chips.&quot; A wave of relief washed over his face, realizing that his name was never really lost, and that Sandra kept up her end of the promise, returning it to him.</p><p>	Sandra smiled, leaned across the table to stop the recording, and thanked Jerome for coming and being forthright with her from the beginning. She wished him the best of luck for the future, and told him that her colleagues from the FAA or NTSB might be in touch to conduct follow up interviews.</p><p>	In fact, another such request by a new group of aviation experts came up later in the week, and when he drove over to the same Office of Aviation to meet with them, he told them about his initial interview with Ms. Sandra Hugo. But nobody recognized that name or had even heard of a Human Performance expert at any of the field offices. Jerome wondered if the woman with short, curly hair that he met had taken away his memory of their time together, or perhaps had swapped that memory with someone else. But rather press the issue with the new group of investigators, he just chocked it up to the stress of the incident taking a toll on his memory, and he apologized for having misremembered.</p><p>	&quot;That&apos;s OK, man,&quot; said the FAA investigator. &quot;I know you&apos;ve been going through a lot, but let&apos;s get started with it OK? Can you please state your name clearly into the recorder, and spell it for us please?&quot;</p><hr><p>	The incident happened three years ago, and Jerome had been staying out of hot water since then. He had to take several more months of remedial training to get out of clearance control, and his physician ordered that he start sleeping with a CPAP machine to help improve the quality of sleep at night. He was able to be reassigned to Houston TRACON, which meant he was closer to his children and had a less stressful job than working in a tower. Jerome still had to stay on his toes for the control center, but unlike the tower job he had in Austin, he simply routed air traffic that was already flying, and there were advanced radar systems that made it easy to keep his eyes on all the flights going through his assigned segment of airspace.</p><p>	He had not thought about his days at the Austin control tower for a very long time, or Sandra Hugo for that matter, until an unusual interaction he had over the radio with a pilot checking in. It sounded like a young woman, who was flying a Cirrus SR-22 to the west at 10,500 feet, who was undergoing some sort of stressful situation.</p><p>	&quot;Houston Center, Cirrus two-juliet-delta, with a question.&quot;</p><p>	&quot;Two-juliet-delta, Houston Center. Go ahead.&quot;</p><p>	&quot;I&apos;m really embarrassed to admit this, but I forgot where I&apos;m going.&quot;</p><p>	&quot;Two-juliet-delta, your flight plan has you direct to KMRF, that&apos;s Marfa, Texas. Do you need assistance? Are you experiencing hypoxia right now?&quot;</p><p>	&quot;Negative hypoxia. I don&apos;t know what happened, but I just totally forgot where I was going.&quot;</p><p>	&quot;Do you require assistance?&quot; Jerome asked the disoriented pilot.</p><p>	&quot;Negative, I think I&apos;m all set now. Thanks for the help, two-juliet-delta.&quot;</p><p>	&quot;Roger, frequency change to Albuquerque Center, 135.87. Have a safe one.&quot;</p><p>	Jerome handed off the pilot to the controller in the next segment of airspace she was about to enter. Not completely convinced that her forgetfulness was something other than Sandra&#x2019;s spell, though it could have just been dehydration from flying in an unpressurized aircraft for several hours, Jerome called up the center in Albuquerque and explained his interaction with the Cirrus pilot. It turned out that she landed safely in Marfa, on time and without incident. Jerome hung up and felt relaxed because it really did seem like she was going to be OK and eventually found the place she wanted to be.</p><p>	If only one could be so lucky.</p>]]></content:encoded></item><item><title><![CDATA[Global State with Zustand]]></title><description><![CDATA[Zustand is a delightfully easy-to-use library for implementing global state in your React applications.]]></description><link>https://www.stephenmayeux.com/global-state-with-zustand/</link><guid isPermaLink="false">6249d079dfa8d01f6704f5a9</guid><category><![CDATA[React]]></category><category><![CDATA[Tech]]></category><category><![CDATA[Tutorials]]></category><dc:creator><![CDATA[Stephen Mayeux]]></dc:creator><pubDate>Mon, 04 Apr 2022 16:39:53 GMT</pubDate><media:content url="https://storage.stephenmayeux.com/2022/04/bear.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.stephenmayeux.com/2022/04/bear.jpeg" alt="Global State with Zustand"><p>When I started learning React in 2016, the de facto library for managing global state was Redux. It has a steep learning curve and requires quite a bit of boilerplate to get started, but 6 years later and Redux still remains one of the most popular packages to include in a complex frontend application. There are so many supplemental middleware packages to make development easier, such as <a href="https://github.com/reduxjs/redux-thunk">Redux Thunk</a> for asynchronous action creators, and recently the <a href="https://redux-toolkit.js.org/">Redux Toolkit</a> has become &quot;the official, opinionated, batteries-included toolset for efficient Redux development.&quot;</p><p>But for smaller applications, Redux is overkill. There are a lot of alternatives to choose from, and recently I had a very good experience working with <a href="https://github.com/pmndrs/zustand">Zustand</a>. In this tutorial, I will show you how to get started with it and also share my special recipe that you won&apos;t find in the official documentation!</p><h2 id="the-basics">The Basics</h2><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">The tutorial assumes you have a React application set up and running. You should also install Zustand with <code>npm install --save zustand</code> in the root of your application directory.</div></div><p>So let&apos;s get started by building our state object for a dog watching application. Why you should ever need an app for watching dogs is beyond me, but I wanted to have a simple example for this tutorial. In real life, just live in the present moment and enjoy watching dogs playing in the park!</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/55fd9c88435a2459f1e424cf22ec2bf4.js?file=useStore.js"></script><!--kg-card-end: html--><p>The <code>create</code> function from Zustand returns a React hook that can be imported and used in functional components. Hooks can only be used inside of functional components, but later in this tutorial I&apos;ll show you how you can use your Zustand store even if you are using class components.</p><p><code>create</code> takes a function as the first argument and returns an object, which represents the state tree. The returned object can have any number of keys with any types of values. Let&apos;s go over each of the properties and methods in the <code>useStore</code> object.</p><p><code>dogs</code> is a simple property and its starting value is 0. </p><p>The <code>create</code> function&apos;s first argument is another function, and that function has two arguments: <code>set</code> and <code>get</code>. In the <code>toString()</code> method, I call <code>get().dogs</code> to retrieve the current value of <code>dogs</code> and return a formatted string based on the value.</p><p>In <code>incrementDogs</code>, I update the value of <code>dogs</code> by adding one, and I accomplish this by using the <code>set</code> method. As you can see, the <code>set</code> method takes a function as an argument and returns an object, which is then <em>merged </em>with the entire global state object. That merging behavior is very important because it will only update the value of <code>dogs</code> but no other properties in the global state object are affected.</p><p>Additionally, I update the value with <code>state.dogs + 1</code>, which is equivalent to <code>get().dogs + 1</code>. The <code>set</code>&apos;s callback function takes one argument, <code>state</code>, which can be accessed inside of <code>set</code>.</p><p><code>decrementDogs</code> subtracts 1 from our total number of dogs, but it checks if <code>dogs</code> is greater than 0 before any updates occur. It wouldn&apos;t make any sense if we said &quot;Today I saw negative 3 dogs at the park&quot;, so I prevent this from happening in our dog counter.</p><p><code>startOver</code> is a simple method that updates the <code>dogs</code> value to 0.</p><p>And finally, I implement a fake <code>saveProgress</code> method to demonstrate that Zustand doesn&apos;t care if functions are async or not. Just use the async/await keywords, and that&apos;s it! No special middleware required.</p><p>Now that we&apos;ve gone over the state object, let&apos;s import and use it inside of a React component.</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/55fd9c88435a2459f1e424cf22ec2bf4.js?file=app1.jsx"></script><!--kg-card-end: html--><p>First, I import the <code>useStore</code> hook that I created in the last file. It&apos;s a React hook, so I have to call it inside the functional component before the return statement. </p><p><code>const state = useStore()</code> is all that&apos;s needed! Now all the properties and methods in the global state object are accessible from the <code>state</code> variable. As you can see, the component calls <code>state.toString()</code> inside the header element, and the buttons&apos; onClick event handlers call the appropriate method. So easy!</p><div class="kg-card kg-callout-card kg-callout-card-grey"><div class="kg-callout-emoji">&#x1F4A1;</div><div class="kg-callout-text">Now keep in mind that fetching everything from the Zustand store will actually re-render the component every time a property is updated with the <code>set</code> method. This really isn&apos;t a problem with this very simple example because <code>dogs</code> is the <em>only</em> property that gets updated, but your applications will likely have more properties that get updated so you might want to avoid this behavior. Let&apos;s see how in the next example.</div></div><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/55fd9c88435a2459f1e424cf22ec2bf4.js?file=app2.jsx"></script><!--kg-card-end: html--><p>For demonstration purposes, I have moved the buttons into their own component, isolated from the header text. In this version, we are creating multiple state slices by passing in a selector function to the <code>useState</code> hook. This is a very efficient method for making atomic selections (in other words, picking a single property or method from the Zustand state object). In addition, if the <code>dogs</code> value updates then this component won&apos;t re-render because <code>dogs</code> isn&apos;t selected in this buttons-only component.</p><h2 id="improve-it-with-an-hoc">Improve it with an HOC</h2><p>As you can see, it&apos;s really easy to get started with Zustand, and <a href="https://github.com/pmndrs/zustand">the documentation</a> on the GitHub repo gives even more examples than this tutorial. There are a few drawbacks to using Zustand in the examples I have given so far, but luckily for you, I&apos;ve come up with a solution that addresses all the issues I have with it. Let&apos;s first discuss the shortcomings:</p><ol><li>The result of Zustand&apos;s <code>create</code> function is a React hook, which means it can only be used in functional components. A lot of React apps are switching over to functional components anyways, but what if you&apos;re still using classes?</li><li>It&apos;s very easy to select either all the properties from Zustand, or just one property at a time. But what if you wanted to construct a single object with multiple state selections, similar to Redux&apos;s <code>mapStateToProps</code> function? </li><li>In the examples that both the documentation and I have given, we are tightly coupling a component to the Zustand global state. For this simple app, this is probably OK but it doesn&apos;t really make the components reusable. If our components depend on a <code>dogs</code> value, should our component really care about the source of this data? Does it care if it&apos;s from a hook or just good, old-fashioned props? Coupling a component to a global state object makes it harder to write unit tests as well!</li></ol><p>We can address all 3 of these issues by wrapping our component with an HOC, or higher order component. In fact, if you&apos;ve ever used <code>react-redux</code>&apos;s <code>connect</code> HOC, then you know exactly what&apos;s going on here!</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/55fd9c88435a2459f1e424cf22ec2bf4.js?file=app3.jsx"></script><!--kg-card-end: html--><p>In a moment, I&apos;ll show you how I implement the <code>withState</code> HOC, but right now let&apos;s just focus on how we&apos;re using it.</p><p>First, I import <code>withState</code> and invoke it at the bottom of the file with an <code>export default</code> statement. The argument we pass to it is a selector function, which behaves exactly the same as the <code>mapStateToProps</code> function that gets passed to <code>react-redux</code>. I&apos;m simply choosing the properties from the Zustand state object and mapping it to properties that will be passed to our component via props.</p><p>The mappings can have the exact same names, or I can provide entirely new names. In this case, I have decided to rename <code>toString</code> to <code>numDogsToString</code> and now this method will be available via props. So instead of using <code>state.toString()</code> as you saw in the first example, I can now use <code>props.numDogsToString()</code>.</p><p><code>withState(selectors)</code> actually returns a function, so I invoke the returned function and pass in the component I want to &quot;connect&quot; to the Zustand global state. The result looks like <code>withState(selectors)(DogWatcher)</code>. Now let&apos;s see how I implement the withState HOC.</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/55fd9c88435a2459f1e424cf22ec2bf4.js?file=withState.jsx"></script><!--kg-card-end: html--><p>In order to construct a new object with multiple state picks, I &quot;instruct&quot; Zustand to diff the object shallowly by passing in Zustand&apos;s shallow function, which I import at the top of the file.</p><p>Now remember, when you invoke <code>withState</code> you have to pass in a selector function that returns an object. This object&apos;s keys are mapped to the component&apos;s props, and there&apos;s a possibility of having colliding or duplicate prop names. For example:</p><pre><code class="language-JSX">// Default export connected to Zustand
import DogWatcher from &apos;./dogWatcher&apos;;

&lt;SomeParentComponent&gt;
  &lt;DogWatcher
    numDogsToString={() =&gt; console.log(&apos;Could possibly be overwritten&apos;)}
  /&gt;
&lt;/SomeParentComponent&gt;</code></pre><pre><code class="language-JSX">import withState from &apos;./withState&apos;;

function DogWatcher(props) {
	return &lt;h1&gt;{props.numDogsToString()}&lt;/h1&gt;
}

const selectors = (state) =&gt; ({
  numDogsToString: state.toString
  /** This collides with props passed from &lt;SomeParentComponent&gt; */
  /** This should be renamed. */
});

export default withState(selectors)(DogWatcher);</code></pre><p>So for this reason, I implement a very simple check that alerts the developer to colliding names with a function called <code>hasUniqueKeys</code>. And then finally, the HOC returns the component along with its existing props as well as the Zustand store also as props.</p><p>That&apos;s it! For small to mid-sized apps that has a relatively small global state tree, Zustand is a very easy and fun solution to use. Its size is relatively small and will not bloat the size of your React bundle.</p>]]></content:encoded></item><item><title><![CDATA[How I Became a Software Engineer]]></title><description><![CDATA[The story of how I transitioned from teaching English to becoming a software engineer, plus all of your questions answered.]]></description><link>https://www.stephenmayeux.com/becoming-a-software-engineer/</link><guid isPermaLink="false">622ce2dbdfa8d01f6704f09d</guid><category><![CDATA[Career]]></category><category><![CDATA[Articles]]></category><category><![CDATA[Personal]]></category><dc:creator><![CDATA[Stephen Mayeux]]></dc:creator><pubDate>Wed, 16 Mar 2022 04:49:49 GMT</pubDate><media:content url="https://storage.stephenmayeux.com/2022/03/busan-start-up.jpeg" medium="image"/><content:encoded><![CDATA[<img src="https://storage.stephenmayeux.com/2022/03/busan-start-up.jpeg" alt="How I Became a Software Engineer"><p>Sometimes when I&apos;m alone, usually when I&apos;m going for a walk or taking a break from work, I wonder to myself, &quot;How the heck did I get here?&quot;</p><p>I&apos;m very fortunate to tell you that life is really good for me right now. Software engineering is a career that I absolutely love doing, and if you love what you do, you never work a day in your life. It has also been very lucrative, which has enabled me to pay off massive amounts of debt and achieve financial stability. </p><p>But my adult life hasn&apos;t always been like this. In my 20s, I could barely manage to make ends meet with the low-paying teaching jobs I had, much less pay off any debt or even save a small amount of money for a rainy day. And although I had a heartfelt devotion to teaching and helping my students learn the English language, I slowly became disillusioned with teaching as a profession and started to resent the school administrators who were running the show.</p><p>Nowadays a lot of people are asking me the same question. &quot;I want a career in tech as well, so how the heck did you get there?&quot; I love telling my story because it has the potential to inspire other people to do the same thing and change their lives dramatically. I have spoken to a lot of friends and acquaintances about my professional journey as well as complete strangers on Reddit, and some of these people have set out on becoming software engineers.</p><p>They ask a lot of great questions much better than &quot;How did you get there?&quot; So in this article, I want to tell the story of how I became a software engineer by answering the most common questions people ask me.</p><h2 id="how-long-does-it-take">How long does it take?</h2><p>It took me about <strong>1,500 hours</strong> to learn the entry level skills required to land my first job as a software engineer. On average, I spent about 20 hours every week learning how to code for a period of <strong>18 months</strong>.<strong> </strong>I kept detailed notes about what I studied and practiced on a daily basis, and I think this accurate record keeping was a vital part of my success.</p><p>Every now and then I see posts on LinkedIn from people who claim they took as little as 6 months of learning before finding their first jobs. While this is in the realm of possibility, it is not very realistic for most people to land their first job as a software engineer after 6 months of self-studying or starting a bootcamp. Usually these people have quit their jobs and devote every waking hour to learning how to code, and for many adults, it&apos;s not possible to support themselves for this long without a job. Even if you had six months of living expenses set aside so that you could focus on studying full-time, it still wouldn&apos;t be enough time for most people to find their first jobs. </p><p>The demand for mid-level engineers (about 2-3 years of experience) and senior level engineers (5 or more years of experience) cannot be met with the current supply of software developers who are actively looking for a job. The problem is most companies only want to hire mid and senior level software engineers, and there are relatively fewer positions for junior engineers (no experience, up to 2 years experience).</p><p>Because finding an entry-level software engineering job requires a wide breadth of knowledge and because the market is saturated with junior engineers seeking their first jobs, <strong>1 - 2 years is a more realistic timeframe</strong>.</p><figure class="kg-card kg-image-card kg-card-hascaption"><img src="https://storage.stephenmayeux.com/2022/03/2016-07-12-18.30.26-1.jpg" class="kg-image" alt="How I Became a Software Engineer" loading="lazy" width="3264" height="2448"><figcaption>Recovering from appendicitis did not interrupt my studying. Busan, July 2016&#xA0;</figcaption></figure><h2 id="what-was-your-study-routine-like">What was your study routine like?</h2><p>During most of the 18-month period while I was learning to code, I was still working as an English teacher in South Korea. I was employed at a language academy for adults and taught from 2pm - 10pm Monday - Fridays, and about 1 or 2 Saturdays every month. I would get home around 11pm, go for a 5k run, take a shower, and then scarf down some steamed dumplings with salsa (Yes, I know this sounds like a disgusting combination, but it was actually quite delicious!)</p><p>From <strong>midnight to 4am</strong>, I would log on to FreeCodeCamp or Udemy, and either watch coding tutorials, complete algorithm challenges, or work on small projects to add to my portfolio. I would go to sleep and wake up around 11am or noon to get ready for work, and then repeat the same routine all over again. The subway ride to and from work usually took about 40 minutes, and I remember watching coding videos on my phone or working through algorithm challenges with just pen and paper. I would study coding during my hour-long break at work, and if a class was cancelled at the last minute, I would sneak off to a quiet room in the school and study more coding.</p><p>I wanted to avoid burn out, so I usually took Friday and Saturday nights off from learning to go out and spend time with my friends. In Korea, the expat community is very closely-knit and the nightlife is cheap, and I think the combination of having many friends and affordable entertainment kept me from keeling over from exhaustion!</p><p>Most of my studying was spent alone, but I started the Busan chapter of FreeCodeCamp so that I could <strong>learn with other people</strong>. We met every 2nd and 4th Sunday of the month and spent about 3 or 4 hours together learning in small groups. Since I was the meet up organizer, a lot of people looked up to me to teach them coding, and even though I was a beginner myself, I found that <strong>teaching other people how to code</strong> vastly improved my skills and accelerated my learning. </p><p>I was having a particularly difficult time at my teaching job during this period of my life, and these coding meet ups gave me something to look forward to. The members of this group looked up to me as their leader and as the glue that held everyone together, and for the first time in my life, I felt a strong sense of connection and belonging to a group of people. These coding meet ups and the people who participated make up some of the fondest memories of my life, and my eyes still well up in happy tears when I look back at the pictures.</p><figure class="kg-card kg-gallery-card kg-width-wide kg-card-hascaption"><div class="kg-gallery-container"><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/2015-08-23-11.59.15.jpg" width="2048" height="1536" loading="lazy" alt="How I Became a Software Engineer"></div><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/2015-09-19-14.27.46-1.jpg" width="3264" height="2448" loading="lazy" alt="How I Became a Software Engineer"></div><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/2015-10-18-13.35.11.jpg" width="2048" height="1536" loading="lazy" alt="How I Became a Software Engineer"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/2015-10-25-13.53.24.jpg" width="3264" height="2448" loading="lazy" alt="How I Became a Software Engineer"></div><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/2015-11-08-16.20.11.jpg" width="3264" height="2448" loading="lazy" alt="How I Became a Software Engineer"></div><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/IMG_5375.jpeg" width="5184" height="3456" loading="lazy" alt="How I Became a Software Engineer"></div></div><div class="kg-gallery-row"><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/IMG_5443.jpeg" width="5184" height="3456" loading="lazy" alt="How I Became a Software Engineer"></div><div class="kg-gallery-image"><img src="https://storage.stephenmayeux.com/2022/03/IMG_8096.JPG" width="5184" height="3456" loading="lazy" alt="How I Became a Software Engineer"></div></div></div><figcaption>FreeCodeCamp Busan, circa 2015 - 2016</figcaption></figure><h2 id="how-did-you-find-your-first-job-and-how-did-you-know-you-were-ready-to-start-applying">How did you find your first job? And how did you know you were ready to start applying?</h2><p>I never felt ready to start applying to jobs because there are so many fundamentals to learn. But I had bills to pay, and I had to start applying sooner or later.</p><p>I moved back to the States in September 2016, and at this point I had been learning to code for about 14 or 15 months. I only had 3 months of living expenses set aside, and so I felt intense pressure to find a full-time coding job by the end of the year.</p><p>For every software engineering job that I have been offered, including my very first job, it was because I <strong>leveraged LinkedIn</strong> properly. Believe it or not, I&apos;ve never actually applied to a job by submitting an application, and I do not recommend junior engineers doing so. Submitting an application is &quot;going through the front door&quot;, and when you walk through the same front door that hundreds of more experienced developers go through, you&apos;re just not going to get scheduled for an initial interview. The odds are not in your favor, and it&apos;s so heartbreaking to see posts from struggling junior developers on LinkedIn. Many of them admit to applying to hundreds and hundreds of jobs over several months without so much as an acknowledgement.</p><p>So how did I find a job through LinkedIn without actually applying? Well, long story short, I built an audience by <strong>posting interesting content</strong> and started to engage with the people who viewed and commented on my posts. During the summer of 2016, when I was wrapping up my final months in Korea, I was creating short JavaScript videos and uploading them to YouTube. I shared my videos on Twitter and LinkedIn with the relevant descriptions and hashtags, and people in the industry started noticing and following me.</p><p>I took advantage of a free month of LinkedIn Premium, which allowed me to see a list everybody who had viewed my profile. I reached out to these people directly and messaged them matter of factly: &quot;Hey, I noticed that you checked out my profile, and I was wondering if your company is hiring junior software engineers.&quot; That worked surprisingly well and I was able to land several interviews that way.</p><figure class="kg-card kg-embed-card"><iframe width="200" height="150" src="https://www.youtube.com/embed/pnUjKTgMhe8?feature=oembed" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe></figure><p>One very small start up company in Austin, Texas gave me the opportunity to complete a take-home coding exercise. They wanted me to build a simple web application from scratch and deploy it to the Internet within a week&apos;s time. It was actually quite a challenging assignment for me, and I was unable to complete it on time. But after that failed attempt, I brushed up on the skills that I was lacking and then I resubmitted the assignment to the hiring manager about 3 months later. I wrote something along the lines of &quot;Hey, do you remember me? I tried completing the interview coding assignment 3 months ago, but I couldn&apos;t finish it on time. I studied up on Node.js and React and I was able to complete it now. I don&apos;t expect you to offer me another interview, but I wanted to let you know.&quot;</p><p>My follow up blew them away! They never had a rejected candidate redo the assignment and share it with them, so they gave me a completely new coding assignment, again with a one-week deadline. I was able to complete it and sure enough they offered me a job! A week later, a start up company in Germany also offered me a job, but after having returned home from living in Korea for 2 years, I was ready to settle down in the States and not become an expat again.</p><p>I drove to Austin in my Toyota Yaris all the way from Maine, and I started my first full-time job as a software developer on Tuesday, November 8, 2016 &#x2013; Election Day. That first job ended up being a bad fit for me. It offered no health insurance, which I desperately needed to have, and I didn&apos;t get the support and mentorship from experienced developers. I parted ways with them just a couple of months later. My last day was Friday, January 20, 2017 &#x2013; Inauguration Day. </p><p>It all worked out in the end though. Fortunately, I had a local recruiter from InMotion Software reach out to me on LinkedIn, and he set up an interview. I had a similar take-home coding assignment and a second on-site interview at their office. They offered me a job, and I ended up staying with that company for about 2 and a half years. And the rest is history!</p><h2 id="do-i-need-to-enroll-in-a-bootcamp-how-about-a-cs-degree">Do I need to enroll in a bootcamp? How about a CS degree?</h2><p>If you think you would benefit from a more traditional learning environment with an instructor, classmates, and assignments then by all means enroll in a bootcamp. They are quite expensive though, and despite their exaggerated marketing claims, you&apos;ll unlikely find a job so soon after graduating from the bootcamp. Furthermore, companies do not care about this at all. They don&apos;t need to see your certificate of completion, and it doesn&apos;t matter to them if you attended a &quot;prestigious&quot; or &quot;competitive&quot; bootcamp.</p><p>What matters most? Your ability to <strong>solve problems with code</strong> is the most important thing. It doesn&apos;t matter if you got your education from YouTube or from Stanford University.</p><p>If you decide to enroll in a bootcamp, please do your due diligence and research them extensively. There have been several high-profile cases of for-profit bootcamps that actually do little to prepare students to enter the job market, yet they still charge exorbitant tuition fees. Some bootcamps will offer tuition deferment, but this is an extremely bad idea! In return for &quot;free tuition&quot; to enroll in the program, you agree to give the school up to 30% of your salary for a period of 1 to 2 years. It seems like a great deal at first, but an additional 30% deduction on top of taxes doesn&apos;t leave much left over at the end of the month.</p><p>My advice is to <strong>skip bootcamps</strong> unless you already have the tuition saved up and you would benefit greatly from having the support of an instructor.</p><h2 id="what-resources-did-you-use-and-what-kind-of-coding-do-you-do-now">What resources did you use? And what kind of coding do you do now?</h2><p>I am a full stack web developer, which means I create web applications. I am able to work on the frontend (what the user sees in the browser) as well as the backend (all of the business logic on the server and databases). Early on in my learning journey, I decided that I wanted to learn full stack web development with JavaScript because 1) FreeCodeCamp has an excellent curriculum for learning this and 2) it only required me to learn one programming language for both the frontend and backend.</p><p>If you want to code web applications, then I highly recommend you learn JavaScript. It may also be useful to learn another language for the backend, such as PHP, Python, Ruby, or even Java.</p><p>I used free or very inexpensive courses from Udemy to learn how to code. Here is what I recommend:</p><!--kg-card-begin: html--><ul>
    <li><a href="https://freecodecamp.org" target="_blank">FreeCodeCamp</a> - Amazing, free content with an excellent community. The content is open-source and relies on volunteers, and I recommend you supplement with other materials.</li>
    <li><a href="https://www.udemy.com/course/the-web-developer-bootcamp/" target="_blank">Colt Steele&apos;s The Web Developer Bootcamp</a> - Literally everything you need to learn for full stack web development with JavaScript. Over 60 hours of content. Wait for a sale and purchase this course for under $20.</li>
        <li><a href="https://www.udemy.com/course/understand-javascript/" target="_blank">Anthony Alicea&apos;s JavaScript: Understanding the Weird Parts</a> - This course made JavaScript click for me!</li>
        <li><a href="https://www.udemy.com/course/react-redux/" target="_blank">Stephen Grider&apos;s Modern React with Redux</a> - My favorite instructor on Udemy. I have purchased all of his courses because he is such a great instructor. This course taught me everything I needed to know about React, and it has since been updated to include the newest features.</li>
</ul><!--kg-card-end: html--><h2 id="reach-out">Reach Out</h2><!--kg-card-begin: html--><p>The blog is very new, and I&apos;m working on it during my free time. I&apos;ll eventually implement a commenting system or contact form, but in the mean time, feel free to contact me on <a href="https://linkedin.com/in/stephenmayeux" target="_blank">LinkedIn</a>. Let me know what you think about this article, and ask me more questions! I imagine I&apos;ll be updating my story often based on your feedback.</p><!--kg-card-end: html-->]]></content:encoded></item><item><title><![CDATA[Conceal Spoilers with a Custom React Component]]></title><description><![CDATA[Create a custom React component that conceals spoilers, but allows users the ability to click and reveal the contents.]]></description><link>https://www.stephenmayeux.com/react-spoiler-text/</link><guid isPermaLink="false">6226c91bdfa8d01f6704eebf</guid><category><![CDATA[React]]></category><category><![CDATA[Tutorials]]></category><category><![CDATA[Tech]]></category><dc:creator><![CDATA[Stephen Mayeux]]></dc:creator><pubDate>Tue, 08 Mar 2022 04:32:43 GMT</pubDate><media:content url="https://storage.stephenmayeux.com/2023/06/secrets.png" medium="image"/><content:encoded><![CDATA[<img src="https://storage.stephenmayeux.com/2023/06/secrets.png" alt="Conceal Spoilers with a Custom React Component"><p>People love to discuss their favorite TV shows, movies, and books on the Internet. My favorite place to do this is on Reddit, but if I&apos;m not careful and don&apos;t alert other users to possible spoilers in a post or comment that I&apos;ve written, then I will be downvoted into oblivion. </p><p>Luckily for us, Reddit has a neat markdown feature that allows people to conceal text from other Redditors, but it reveals the secret text once it has been clicked. It&apos;s a very simple solution, and I will show you how it&apos;s done in React.</p><!--kg-card-begin: html--><div id="spoiler-tutorial-root"></div><!--kg-card-end: html--><p>Let&apos;s start with a very minimal React application. I&apos;ve created the main <code>App</code> component and have imported our custom <code>SpoilerText</code>. As you can see, it wraps around other JSX elements.</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/6916209c9bb74b262d53a0eda1cee297.js?file=index.jsx"></script><!--kg-card-end: html--><p>Implementing it is very easy. The JSX elements that are wrapped around <code>SpoilerText</code> are accessed with <code>props.children</code>. We wrap the child elements in a <code>&lt;div&gt;</code> that has a CSS class applied to it: either <code>.App-hidden</code> or <code>.App-notHidden</code>. </p><p>We keep track of whether it&apos;s hidden or not with the <code>useState</code> hook. Initially this <code>hidden</code> value is true when the component is rendered for the first time, but as soon as the user clicks on the element, the <code>reveal</code> function is invoked and it updates the value to false.</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/6916209c9bb74b262d53a0eda1cee297.js?file=SpoilerText.jsx"></script><!--kg-card-end: html--><p>Now all you need is a bit of CSS to complete the effect.</p><!--kg-card-begin: html--><script src="https://gist.github.com/StephenMayeux/6916209c9bb74b262d53a0eda1cee297.js?file=styles.css"></script><!--kg-card-end: html--><p>To recursively select all child elements that are inside of a class called <code>.App-hidden</code>, then use the <code>&gt; *</code> css selector. We set the background-color and text color to black, so that it appears to have been censored and redacted. For a better UI experience, I have added a <code>cursor: pointer</code> rule so that a user knows the element is clickable when they hover their mouse pointer over the hidden text.</p><p>I have also added the <code>::selection</code> pseudo class, so that a spoiler is not accidentally revealed if the user selects or highlights the text.</p><p><code>.App-notHidden</code> simply unsets all of the CSS properties that were applied in <code>.App-hidden</code>, and this is all achieved with a simple animation. By using <code>transition: all 1s</code>, the text and background color slowly fade back to defaults. You can control the speed of this and use decimal values or even milliseconds.</p><p>That&apos;s it! Pretty easy, right?</p><p>This component can be extended and support more features very easily. For example, the default color is black and the default animation speed is 1 second. But you could very easily accept optional props to customize the look and feel of this component</p>]]></content:encoded></item></channel></rss>