<?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: Yash Mahalwal</title>
    <description>The latest articles on DEV Community by Yash Mahalwal (@yashmahalwal).</description>
    <link>https://dev.to/yashmahalwal</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%2F1044229%2Ffaeb4649-239d-45cd-ae04-7fa73db3cd27.jpeg</url>
      <title>DEV Community: Yash Mahalwal</title>
      <link>https://dev.to/yashmahalwal</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/yashmahalwal"/>
    <language>en</language>
    <item>
      <title>Lessons from remote work on burnout, balance, and well-being</title>
      <dc:creator>Yash Mahalwal</dc:creator>
      <pubDate>Sun, 24 Aug 2025 12:37:00 +0000</pubDate>
      <link>https://dev.to/yashmahalwal/lessons-from-remote-work-on-burnout-balance-and-well-being-5c84</link>
      <guid>https://dev.to/yashmahalwal/lessons-from-remote-work-on-burnout-balance-and-well-being-5c84</guid>
      <description>&lt;p&gt;It usually begins with the same question. Someone asks me where I work, and I already know what’s coming next. I take a small pause and say, “I work remotely.” Their face lights up, just as I expect. “You’re so lucky!” they reply, almost every time, as if I’ve been handed the perfect life. I smile back, because it’s easier than explaining. On the surface, yes, remote work sounds like freedom — no commute, more flexibility, home comforts. But as I hold that smile, there’s always another thought in my mind: if only they knew what it really feels like.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@yasmina" rel="noopener noreferrer"&gt;Yasmina H&lt;/a&gt; on Unsplash&lt;/em&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%2Fpxzy3e1r1h6hbtq3r55o.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%2Fpxzy3e1r1h6hbtq3r55o.png" alt="Remote worker staring out the window" width="800" height="530"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;Before I go further, I want to make it clear that this is only my perspective, my story. I’m a 27-year-old Indian software engineer, living alone in a city, and I’ve been working remotely for almost five years now. I’m also gay, which shapes the way I experience isolation and community in its own way. Your journey may look very different, but I think some parts of mine might feel familiar. And even if they don’t, I hope it offers you a useful point of view. In this article, I want to talk about the side of remote work that we rarely discuss — the burnout, the loneliness and the health struggles that build up over time. I also want to share how I eventually found some balance through it all. This isn’t a guide or a set of rules, only my experiences and the things that helped me.&lt;/p&gt;

&lt;h2&gt;
  
  
  Physical Health
&lt;/h2&gt;

&lt;p&gt;Working from home meant my days had no structure. I lived alone, with no routine to guide me. Slowly my body started to pay the price. My sleep cycle was a mess. Some nights I stayed awake until morning and went to bed at 8 a.m. I ordered food often and ate out a lot. Combined with stress, weight gain came easily.&lt;/p&gt;

&lt;p&gt;The bigger problem was my gut. Stress and lifestyle together hit me hard. I developed chronic gut issues that made every morning difficult. Gut problems like this are also fairly common in sedentary, stressful jobs. I have friends dealing with IBS, gastritis, even hemorrhoids. Almost every day, I woke up with severe burning and cramps. It often took me up to three hours just to let the pain settle before I could move forward. This also made it impossible to settle into any kind of routine. And it only made my mental health worse, since the two are so closely linked.&lt;/p&gt;

&lt;p&gt;In fact, a UK study found that full-time remote workers took about &lt;strong&gt;10 sick days a year compared to 5 for hybrid workers&lt;/strong&gt;, and were also more likely to face obesity and depression. (&lt;a href="https://www.thetimes.co.uk/article/working-from-home-full-time-raises-risk-of-being-fat-and-depressed-8q5zfsc67" rel="noopener noreferrer"&gt;The Times&lt;/a&gt;)&lt;/p&gt;

&lt;h3&gt;
  
  
  What helped me
&lt;/h3&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@gabinvallet" rel="noopener noreferrer"&gt;Gabin Vallet&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;br&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%2F85o8y4lynwz1y8fr9vkb.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%2F85o8y4lynwz1y8fr9vkb.png" alt="Exercise and movement" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Movement became my friend. I started going out for walks and runs, and added some form of cardio to my days. The endorphins helped my body feel better and kept my mind lighter. Going to the gym also made a difference. It helped me stay in shape, and it also meant I could see people and break the cycle of isolation.&lt;/p&gt;

&lt;p&gt;I tried to stick to a strict routine. Sleeping and waking up at the right time helped bring some order back into my days. I still struggle with this at times, especially when work runs late, but even small improvements in my sleep made a big difference.&lt;/p&gt;

&lt;p&gt;Food was another area I worked on. I began eating at the right times, keeping my meals simple and clean. A fiber-rich breakfast, more salads, probiotic foods, and staying hydrated all helped my gut. I also started keeping a gap between dinner and sleep, which gave my body time to settle. Simple lifestyle changes ended up making major differences.&lt;/p&gt;

&lt;p&gt;I also saw a doctor and committed to a healthier lifestyle. I quit smoking and drinking, which not only helped my physical health but also gave me more clarity mentally.&lt;/p&gt;

&lt;h2&gt;
  
  
  Mental Health
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@punttim" rel="noopener noreferrer"&gt;Tim Gouw&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;br&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%2Fiosxjcdiux314y7mhhzk.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%2Fiosxjcdiux314y7mhhzk.png" alt="Exhausted remote worker" width="800" height="534"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;One of the first problems I faced with remote work was with my mental health. I started working young, around 20. I learned how to do the job well, but I never learned how to handle the stress that came with it. Remote work made this worse.&lt;/p&gt;

&lt;p&gt;The line between work and home disappeared. I ate, slept, and lived in the same place where I worked. There was no change of scene and no escape. Work hours were also never fixed. It was too easy to open my laptop at night for “just one quick fix.” Days off were rare. Working from home already felt like a break, so I often pushed myself to keep going.&lt;/p&gt;

&lt;p&gt;Daily interactions also vanished. There was no commute, no colleagues to meet, no small breaks that came naturally in an office. My routine was built around myself. Most of the time, it was just me in my apartment. I had a roommate for a while, but they were usually at work or busy with their own life. When I finished work, I often didn’t know what to do. I felt too mentally drained to pick up hobbies. Even watching a movie felt like too much. Endless reels wrecked my attention span, so nothing ever held me for long. Over time, it evolved into a slow kind of isolation. I also had a history of depression. Remote work made it spiral out of control. Anxiety came with it, and the mix became crippling. It began to affect my daily life in ways I couldn’t ignore.&lt;/p&gt;

&lt;p&gt;I also know people who slipped into substance abuse while working from home. With alcohol or other substances always within reach, it’s easy to pour a drink during work or in a long meeting. What feels harmless in the moment can slowly turn into a routine without them even noticing.&lt;/p&gt;

&lt;h3&gt;
  
  
  What helped me
&lt;/h3&gt;

&lt;p&gt;The first change I made was to give work its own space. I set up a separate room for it. All work stayed in that room. When I felt overwhelmed, I would step out and shut the door. That simple act helped me leave work behind. I also set strict hours. Once my day ended, I turned off notifications and closed the laptop.&lt;/p&gt;

&lt;p&gt;I got a cat, which gave me company and comfort. (If you consider getting a pet, ensure that you are not allergic to it.) I started going for walks and working out to bring more movement into my days. I practised mindfulness and simple breathing exercises, which helped me slow down when my thoughts felt too heavy. All of this was a way to step out of my head. Staying in your head all day is a recipe for disaster.&lt;/p&gt;

&lt;p&gt;I began replacing empty screen time with books. That made me feel more grounded and slowly improved my attention span. And one of the biggest steps I took was starting therapy. It gave me tools to handle stress and the cycle of depression and anxiety.&lt;/p&gt;

&lt;h2&gt;
  
  
  Social Life
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@sammoghadam" rel="noopener noreferrer"&gt;Sam Moghadam&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;br&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%2Ffhjbxnn0ddf5wdhci121.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%2Ffhjbxnn0ddf5wdhci121.png" alt="Lonliness" width="800" height="449"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Remote work left me with almost no social life. After work I often felt like doing something, but at home there wasn’t much to do beyond watching Netflix. In an office, you naturally step out for dinners or drinks with teammates. That part just disappeared.&lt;/p&gt;

&lt;p&gt;A survey found that &lt;strong&gt;25% of remote workers say their social skills declined&lt;/strong&gt;, and &lt;strong&gt;20% reported worse mental health&lt;/strong&gt; since working from home. About &lt;strong&gt;two-thirds (66%)&lt;/strong&gt; say this was because of reduced social connection. (&lt;a href="https://nypost.com/2024/12/28/business/25-of-remote-workers-social-skills-have-declined-while-working-from-home-survey/" rel="noopener noreferrer"&gt;New York Post / ResumeBuilder&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I have a friend who got so used to being at home that even daily interactions like buying groceries felt overwhelming. I feel it too. In a new setting, starting interactions is hard for me, and I often end up staying quiet even when I want to connect.&lt;/p&gt;

&lt;p&gt;Dating can sometimes help fill that gap, but for me it hasn’t been easy. In India, especially in cities, people are open about their sexuality and willing to date. The challenge is that the pool is still small, and finding a good match isn’t simple.&lt;/p&gt;

&lt;h3&gt;
  
  
  What helped me
&lt;/h3&gt;

&lt;p&gt;The biggest change came from finding community. Making connections outside of work gave me something to look forward to. I tried hobby classes in my free time, which helped me meet people and break the routine. For me, queer events like sports, karaoke, crafts, and parties created a space where I felt at home.&lt;/p&gt;

&lt;p&gt;It also helped to find others with remote jobs. I have friends in the same city who work remotely, and on boring days we meet and work together from a café. That small change brings energy back into the day.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://www.pexels.com/@ron-lach/" rel="noopener noreferrer"&gt;Ron Lach&lt;/a&gt; on Pexels&lt;/em&gt;&lt;br&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%2Fiqma7spz62k3onmvkyyu.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%2Fiqma7spz62k3onmvkyyu.png" alt="Working from nature" width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Travel has also been a huge benefit. I often travel with a childhood friend who works remotely too. Together we have seen the Himalayas, the deserts of Ladakh, the beaches of Goa and the forests of the Kerala. I also try to take solo trips and explore new cultures. Many countries now offer digital nomad visas, so take the chance and travel to new places.&lt;/p&gt;

&lt;p&gt;It is also worth exploring where you want to live. I moved to a calm, green neighborhood with plenty of cafés and everything within reach. When I feel tired, I just step out for a walk and it makes me feel better. That would not have been possible if I had to live close to an office.&lt;/p&gt;

&lt;p&gt;Remote work can take away your social life if you let it, but it also gives you freedom to build a better one if you choose it.&lt;/p&gt;

&lt;h2&gt;
  
  
  Workplace
&lt;/h2&gt;

&lt;p&gt;&lt;em&gt;Photo by &lt;a href="https://unsplash.com/@cwmonty" rel="noopener noreferrer"&gt;Chris Montgomery&lt;/a&gt; on Unsplash&lt;/em&gt;&lt;br&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%2Fjas60exrvnnc94ibx7ng.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%2Fjas60exrvnnc94ibx7ng.png" alt="Remote workplace" width="800" height="600"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;My workplace shrunk to a desk and a chair in my apartment. The people I worked with were only faces behind screens, and many times not even that because cameras stayed off.&lt;/p&gt;

&lt;p&gt;Collaboration felt harder without a physical space. You miss the whiteboards, the quick discussions, and the sense of working side by side. Managing teams across time zones added to the challenge. It meant long hours, late-night meetings, and waiting for answers that would have been instant in an office.&lt;/p&gt;

&lt;p&gt;For some of my colleagues, working from home also meant living with family. Staying at home all day while trying to balance work and children around them was overwhelming in its own way.&lt;/p&gt;

&lt;h3&gt;
  
  
  What helped me
&lt;/h3&gt;

&lt;p&gt;We had to find a rhythm of collaboration that worked. It took some time, but it is possible to work asynchronously and still get things done. We relied more on text. The classic “this could have been an email” really should have been an email. Meetings became a place where people came together to deliver value. Time is valuable, and treating it that way helped everyone. Remote work can also empower you to be more productive and focused. With the right boundaries and setup, it gives you the freedom to design your day in a way that brings out your best work.&lt;/p&gt;

&lt;p&gt;A Stanford study found that employees working from home were on average &lt;strong&gt;13% more productive&lt;/strong&gt; than their office counterparts, thanks to fewer breaks and less time spent commuting. (&lt;a href="https://www.gsb.stanford.edu/insights/why-working-home-future-looking-bright?utm_source=chatgpt.com" rel="noopener noreferrer"&gt;Stanford University&lt;/a&gt;)&lt;/p&gt;

&lt;p&gt;I also made an effort to connect with colleagues beyond just work. Taking interest in each other’s lives, sharing vacation photos, stories about family, and having fun in virtual events made the screen feel a little less distant.&lt;/p&gt;

&lt;p&gt;Meeting colleagues in real life whenever possible also made a big difference. If they were in another city or country, I tried to pair it with a trip. Those moments built trust and made working together easier.&lt;/p&gt;

&lt;p&gt;Lastly, I worked on making my own space better. A decent ergonomic chair, a monitor, keyboard, and mouse all helped. A good camera and mic are worth it too. Your entire presence is virtual, so it pays to make it effective. Exploring coworking spaces is also a good option. It gives you place to focus and also brings back a sense of the office when you need it.&lt;/p&gt;

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

&lt;p&gt;Remote work is not just comfort and flexibility, and it is not just isolation and stress either. It can hurt your health, your balance, and your social life if you let it. But it can also give you the freedom to rebuild these things in ways that work for you.&lt;/p&gt;

&lt;p&gt;For me, the past five years have been both difficult and rewarding. I have made mistakes, I have struggled, and I have also found small ways to create balance. Some things are still a work in progress, and that’s okay.&lt;/p&gt;

&lt;p&gt;If you are working remotely, I’d love to hear about your own challenges and what has helped you deal with them. Everyone’s story is different, and sharing those stories might just make the rest of us feel a little less alone.&lt;/p&gt;

</description>
      <category>workplace</category>
      <category>remote</category>
      <category>mentalhealth</category>
      <category>career</category>
    </item>
    <item>
      <title>Optimising React Apps</title>
      <dc:creator>Yash Mahalwal</dc:creator>
      <pubDate>Sun, 21 Apr 2024 19:10:41 +0000</pubDate>
      <link>https://dev.to/yashmahalwal/optimising-react-apps-56c6</link>
      <guid>https://dev.to/yashmahalwal/optimising-react-apps-56c6</guid>
      <description>&lt;p&gt;In this article, we try to understand the approach to optimising React applications. We'll work with a real example to explore some of the most common issues that developers face. Then, we'll go through the process of improving our application to tackle those issues effectively. We'll also learn how to make informed decisions and strike the right balance between different possible options.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Note: This is a lengthy article. However, you may already be familiar with some of the topics we discuss. Therefore, I encourage you to selectively read the parts that are relevant to you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h2&gt;
  
  
  Table of contents
&lt;/h2&gt;



&lt;ul&gt;
&lt;li&gt;Table of contents&lt;/li&gt;
&lt;li&gt;Introduction&lt;/li&gt;
&lt;li&gt;
Performance challenges with React

&lt;ul&gt;
&lt;li&gt;Scheduling an update&lt;/li&gt;
&lt;li&gt;Render phase&lt;/li&gt;
&lt;li&gt;Commit phase&lt;/li&gt;
&lt;li&gt;Where is the issue?&lt;/li&gt;
&lt;li&gt;How do we optimise?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Understanding with a real example

&lt;ul&gt;
&lt;li&gt;Monitoring the application&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

No optimisation

&lt;ul&gt;
&lt;li&gt;
Profiling

&lt;ul&gt;
&lt;li&gt;T = 0 - 18s &lt;/li&gt;
&lt;li&gt;T = 18s - 33s&lt;/li&gt;
&lt;li&gt;T = 33s - 60s&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Adding optimisation

&lt;ul&gt;
&lt;li&gt;Trade-off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Add memoization

&lt;ul&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;
Adding optimisation

&lt;ul&gt;
&lt;li&gt;Trade-off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Reduce renders

&lt;ul&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;
Adding optimisation

&lt;ul&gt;
&lt;li&gt;Trade-off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Reduce computation

&lt;ul&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;
Adding optimisation

&lt;ul&gt;
&lt;li&gt;Trade-off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Optimise Updates

&lt;ul&gt;
&lt;li&gt;Understanding how the store is structured&lt;/li&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;
Adding optimisation

&lt;ul&gt;
&lt;li&gt;Trade-off&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;

&lt;/li&gt;

&lt;li&gt;

Optimise Memory

&lt;ul&gt;
&lt;li&gt;Profiling&lt;/li&gt;
&lt;li&gt;What went wrong?&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;li&gt;

Summing up

&lt;ul&gt;
&lt;li&gt;Credits&lt;/li&gt;
&lt;/ul&gt;


&lt;/li&gt;

&lt;/ul&gt;



&lt;h2&gt;
  
  
  Introduction
&lt;/h2&gt;

&lt;p&gt;React has become increasingly popular in recent years for building user applications due to its introduction of declarative programming. Previously, developers had to write code to update different parts of the UI whenever there was a change. This process was difficult to manage at scale and often resulted in bugs as the application evolved. React changed that by defining the UI as a function of state. Now, developers only need to focus on updating the state correctly, and React takes care of updating the UI.&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%2Fsg6byphbxt2mpjaixg3c.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%2Fsg6byphbxt2mpjaixg3c.png" alt="Stack overflow trend for questions asked for frontend technologies over the years" width="800" height="532"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;React is a good tool for building correct UIs quickly. However, the next problem down the line is performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Performance challenges with React
&lt;/h2&gt;

&lt;p&gt;React is not without its limitations. Over the years, it has become increasingly clear that it is difficult to create performant React applications. Let us go over how React updates UI to understand where the problem lies.&lt;/p&gt;

&lt;h3&gt;
  
  
  Scheduling an update
&lt;/h3&gt;

&lt;p&gt;React maintains a tree of your components. These components can have some &lt;a href="https://react.dev/learn/state-a-components-memory" rel="noopener noreferrer"&gt;state&lt;/a&gt;. Developers can request an update to the state using &lt;code&gt;setState&lt;/code&gt;. This instructs React to schedule an update for the component's state. React performs &lt;a href="https://react.dev/learn/render-and-commit" rel="noopener noreferrer"&gt;render and commit&lt;/a&gt; to process the update for the subtree under this component.&lt;/p&gt;

&lt;h3&gt;
  
  
  Render phase
&lt;/h3&gt;

&lt;p&gt;The component is re-evaluated using the new updated state. For function components, this means invoking the function again. React recursively re-evaluates all the children of the component being updated. In the end, React has the output of all components in the tree. React requires this to be done using pure functions which means if rendering is done multiple times with the same state, output should be the same. The output should only depend on the input and no other external factor. Components can describe any side effects that need to run separately using &lt;a href="https://react.dev/learn/synchronizing-with-effects" rel="noopener noreferrer"&gt;useEffect&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Commit phase
&lt;/h3&gt;

&lt;p&gt;The output of the render phase is an updated representation of  DOM under the component subtree (often called Virtual DOM). React now has the outdated DOM for the subtree along with the updated DOM. It has a &lt;code&gt;O(n)&lt;/code&gt; &lt;a href="https://legacy.reactjs.org/docs/reconciliation.html" rel="noopener noreferrer"&gt;algorithm&lt;/a&gt; to patch the outdated DOM and update it.&lt;/p&gt;

&lt;h3&gt;
  
  
  Where is the issue?
&lt;/h3&gt;

&lt;p&gt;The commit phase is very straightforward. Under the imperative paradigm, developers would manually update the DOM which resulted in inefficient updates. React replaced that by carefully only updating what is needed in one go. &lt;/p&gt;

&lt;p&gt;The render phase is usually more computationally expensive since the entire subtree is calculated again. That in itself is not a big problem for a single update. However, when you have multiple updates occurring rapidly, it becomes a real problem. The React team has &lt;a href="https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024#react-compiler" rel="noopener noreferrer"&gt;acknowledged&lt;/a&gt; that and has started working towards mitigating 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%2F5lar8hrcgq8652cz1odh.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%2F5lar8hrcgq8652cz1odh.png" alt="React can re-render too much" width="800" height="38"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Imagine you have a parent component with 3 state variables - &lt;code&gt;s1&lt;/code&gt;, &lt;code&gt;s2&lt;/code&gt; and &lt;code&gt;s3&lt;/code&gt;. It also has 3 children which rely on one of these variables.&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%2Ffyy5l28yuz1y4pjluwu3.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%2Ffyy5l28yuz1y4pjluwu3.png" alt="Example of the component tree" width="441" height="271"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Even if only &lt;code&gt;s1&lt;/code&gt; is changed, the parent and all the children are re-rendered instead of just parent and Child 1. This is fine since the rendering process is pure and re-rendering other children will return the same result. However, when a lot of updates are made to &lt;code&gt;s1&lt;/code&gt;, &lt;code&gt;s2&lt;/code&gt; and &lt;code&gt;s3&lt;/code&gt; rapidly, the performance overhead becomes evident. This is often the case with highly interactive apps (like chat, video conferencing etc).&lt;/p&gt;

&lt;p&gt;React has introduced measures like batching state updates, background concurrent rendering and memoization to tackle this. My opinion is that the best way to solve the problem is by improving their reactivity model. The app needs to be able to track the code that should be re-run on updating a given state variable and specifically update the UI corresponding to this update. Tools like &lt;a href="https://www.solidjs.com/" rel="noopener noreferrer"&gt;solid.js&lt;/a&gt; and &lt;a href="https://svelte.dev/" rel="noopener noreferrer"&gt;svelte&lt;/a&gt;  work in this manner. It also eliminates the need for a virtual DOM and diffing.&lt;/p&gt;

&lt;h3&gt;
  
  
  How do we optimise?
&lt;/h3&gt;

&lt;p&gt;Software optimisation is usually not a simple process. Each change has trade-offs associated with it. Some trade-offs might be acceptable for your use case while others might not be acceptable. This makes optimisation a very specific process. In general, it is repeated iterations of the following steps:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Observe the application under stress.&lt;/li&gt;
&lt;li&gt;Find which parts of the app run into performance issues.&lt;/li&gt;
&lt;li&gt;Debug the root cause of these performance issues.&lt;/li&gt;
&lt;li&gt;Come up with approaches to mitigate those.&lt;/li&gt;
&lt;li&gt;Compare trade-offs between these approaches and select one most suitable to your use case.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Understanding with a real example
&lt;/h2&gt;

&lt;p&gt;Let us consider a stock monitoring application. This mock app monitors rapidly updating stock prices in real-time. It is deployed &lt;a href="https://react-performance-omega.vercel.app/" rel="noopener noreferrer"&gt;here&lt;/a&gt;. For the best experience, you should clone and run the app from the source from &lt;a href="https://github.com/yashmahalwal/react-performance" rel="noopener noreferrer"&gt;here&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%2Fgpuqftjpmc7p3hipw5yz.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%2Fgpuqftjpmc7p3hipw5yz.png" alt="App screenshot" width="800" height="916"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This app has the following sections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Header with navigation to different versions of stock monitoring  &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%2Fft6l3xc184ryg1z5vysx.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%2Fft6l3xc184ryg1z5vysx.png" alt="Header of the app" width="800" height="58"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Buttons to observe/unobserve the stocks and reset the stock data &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%2Fr3m9wqo2edaocaylyt0u.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%2Fr3m9wqo2edaocaylyt0u.png" alt="Actions" width="642" height="208"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A plot of the running average of the last 50 entries &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%2Fhmi91ret2khj56svhwhv.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%2Fhmi91ret2khj56svhwhv.png" alt="Plot of running average" width="800" height="387"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A list of stock price events - contains the name of the stock and the current price &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%2F14id35qbrjat3qllr3gr.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%2F14id35qbrjat3qllr3gr.png" alt="Table of stock events" width="800" height="316"&gt;&lt;/a&gt;&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;A profiler widget which:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;&lt;p&gt;Shows how many renders take place &lt;strong&gt;every second&lt;/strong&gt; and the average render &amp;amp; commit durations of those updates.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Can plot line chart for the number of renders, average render duration and total time spent calculating per second throughout profiling.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;    Total time = (Render + Commit) * Render Count
&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%2Fcffiys44wt24mekszxqg.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%2Fcffiys44wt24mekszxqg.png" alt="Profiler Widget" width="800" height="287"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;To keep things consistent, I will profile each version of the app for &lt;strong&gt;1 minute&lt;/strong&gt;. Stock events are fired at every &lt;code&gt;200ms&lt;/code&gt;. We will observe how the app behaves and iterate over the code to make it perform better.&lt;/p&gt;

&lt;h3&gt;
  
  
  Monitoring the application
&lt;/h3&gt;

&lt;p&gt;The app already has a profiler widget built on the &lt;a href="https://react.dev/reference/react/Profiler" rel="noopener noreferrer"&gt;Profiler API&lt;/a&gt;. The development build of the app is deployed so that monitoring can be done with ease.&lt;/p&gt;

&lt;p&gt;In addition, you can use React developer tools to see component trees and profile them. This will provide a breakup of why each re-render took place and how much time each component took to re-render.&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%2Fifejqitvjc0zjoptekgg.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%2Fifejqitvjc0zjoptekgg.png" alt="Developer tools for monitoring performance" width="800" height="694"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Chrome also has a built-in performance monitor which helps you see the overall behaviour of the application outside React's context&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%2Fjx99esor229mblezzk4e.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%2Fjx99esor229mblezzk4e.png" alt="Chrome performance monitor" width="800" height="876"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  No optimisation
&lt;/h2&gt;

&lt;p&gt;Let us observe the app without any optimisation. To see the performance fluctuations better, you can add an artificial CPU slowdown. I will be working with a &lt;code&gt;6x&lt;/code&gt; slowdown.&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%2Fvb6zdu15jo95g2el9z3d.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%2Fvb6zdu15jo95g2el9z3d.png" alt="CPU Throttling" width="800" height="83"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Go ahead and click on observe. Remember to start profiling before you start observing. When you want to stop, click on &lt;code&gt;Unobserve&lt;/code&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%2Fzbwxy8uvtdq3mpnpypft.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%2Fzbwxy8uvtdq3mpnpypft.gif" alt="App demo" width="1000" height="563"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;Let us look at the plots generated by the profiling widget and understand the trends:&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%2Fpqwseff0m3zu01igd681.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%2Fpqwseff0m3zu01igd681.png" alt="Render Count" width="800" height="420"&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%2Fuoh6xc3rzedctxvky7u3.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%2Fuoh6xc3rzedctxvky7u3.png" alt="Render Duration" width="800" height="421"&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%2F5oooud8x3077s9ilrzic.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%2F5oooud8x3077s9ilrzic.png" alt="Total time spent" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h4&gt;
  
  
  T = 0 - 18s
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Number of renders starts with &lt;code&gt;10-11&lt;/code&gt; renders per second. It stays at that for a while.&lt;/li&gt;
&lt;li&gt;Render duration increases linearly from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;50ms&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Total time spent increases linearly from &lt;code&gt;0&lt;/code&gt; to &lt;code&gt;500ms&lt;/code&gt;. &lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;These trends show that with time, the load on the JS thread increases. Render duration increases which also increases the total time spent. Also, &lt;code&gt;500ms = 10 x 50ms&lt;/code&gt; which means that the JS thread can process all the renders so far.&lt;/p&gt;

&lt;h4&gt;
  
  
  T = 18s - 33s
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;The number of renders fall rapidly from &lt;code&gt;10&lt;/code&gt; to &lt;code&gt;5&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Render duration keeps on increasing almost linearly to &lt;code&gt;110ms&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Total time spent oscillates between &lt;code&gt;500ms&lt;/code&gt; to &lt;code&gt;650ms&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you look closely, these trends are due to the rendering duration increasing over time. This blocks the &lt;code&gt;JS&lt;/code&gt; thread for more time and leaves less available time for other renders per second. Total time spent shows the correlation between decreasing render count and increasing render duration. When &lt;code&gt;JS&lt;/code&gt; thread throttles and reduces the number of renders processed, the total time spent is lesser. However, since the average render duration also increases, the total time spent rendering increases again. &lt;/p&gt;

&lt;h4&gt;
  
  
  T = 33s - 60s
&lt;/h4&gt;

&lt;ul&gt;
&lt;li&gt;Number of renders continues to fall. It falls from &lt;code&gt;5&lt;/code&gt; to an average of &lt;code&gt;3-4&lt;/code&gt; renders towards the end. The decrease is not as sharp as before&lt;/li&gt;
&lt;li&gt;Render duration still keeps on increasing. However, now the growth has slowed down. This is why the render count did not fall as sharply as before.&lt;/li&gt;
&lt;li&gt;Total time keeps on oscillating. The average is still &lt;code&gt;500-600ms&lt;/code&gt; with large fluctuations on either side.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This is simply because the JS thread is close to reaching the throughput limit and hence, the number of renders processed is reduced. Growth in calculations becomes slow. The app becomes laggy and less responsive at this point.&lt;/p&gt;

&lt;p&gt;Here is a screenshot of the performance analysis using Chrome  Inspector. &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%2Fg1eoxk4uko38w799wtn7.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%2Fg1eoxk4uko38w799wtn7.png" alt="Chrome Inspector" width="800" height="701"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you look at the top, there are a lot of lagging frames. These updates are displayed but they lag behind the main thread. Those are coloured yellow and labelled as &lt;a href="https://developer.chrome.com/blog/new-in-devtools-100#perf" rel="noopener noreferrer"&gt;partially presented frames&lt;/a&gt;. JS thread is busy for &lt;code&gt;92%&lt;/code&gt; of the observation time. &lt;/p&gt;

&lt;p&gt;Now let us take a look at the React profiler under Chrome Inspector.&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%2Fdqagftulgt3sgj254jvu.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%2Fdqagftulgt3sgj254jvu.png" alt="Hook 1 changed" width="800" height="809"&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%2Fy84p93s1q07cv93kryt5.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%2Fy84p93s1q07cv93kryt5.png" alt="Hook 2 changed" width="800" height="789"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This further tells us two things:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The &lt;code&gt;Table&lt;/code&gt; component is the most expensive to render&lt;/li&gt;
&lt;li&gt;During the renders, hooks &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; of the &lt;code&gt;Unoptimised&lt;/code&gt; component change alternatively.&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Adding optimisation
&lt;/h3&gt;

&lt;p&gt;We are familiar with how the app behaves. The table component is the bottleneck. We can start by reducing the time spent rendering it. Let us look at the unoptimised version of the app (&lt;code&gt;src/routes/unoptimised/unoptimised.tsx&lt;/code&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%2Fjy8wra3ow49hzto2pdmr.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%2Fjy8wra3ow49hzto2pdmr.png" alt="Hooks for the unoptimised component" width="800" height="248"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;These are the hooks that React Profiler reported:&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%2Fvf5olbph1zpxb1z0g81f.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%2Fvf5olbph1zpxb1z0g81f.png" alt="Hooks causing re-renders" width="800" height="106"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;code&gt;stockEventList&lt;/code&gt; is used by the table.&lt;/li&gt;
&lt;li&gt;
&lt;code&gt;averagePrices&lt;/code&gt; is used by the line chart plot.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;These are updated alternatively due to useEffect on &lt;code&gt;line 35&lt;/code&gt;. Since they are used to maintain separate parts of the UI, we can add memoization on the children to ensure they only re-render the component they are consumed by. So updating &lt;code&gt;averagePrices&lt;/code&gt; should not re-render the table and updating &lt;code&gt;stockEventList&lt;/code&gt; should not re-render the line chart.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-off
&lt;/h4&gt;

&lt;p&gt;This change will reduce the render durations. Memoization will increase the memory usage. React docs say that it &lt;a href="https://react.dev/reference/react/useMemo#should-you-add-usememo-everywhere" rel="noopener noreferrer"&gt;won't be harmful&lt;/a&gt;. &lt;/p&gt;

&lt;p&gt;It will also introduce refactoring. As a developer, you try to make minimal changes to the codebase. This ensures that existing logic does not break. &lt;strong&gt;However, with React, you'd often be required to prune and refactor big components into smaller ones. This is because your functions map directly to a part of the UI.&lt;/strong&gt;  Solid.js takes an interesting approach to &lt;a href="https://www.solidjs.com/tutorial/introduction_components" rel="noopener noreferrer"&gt;components&lt;/a&gt; as simply a means to modularity and reusability. Breaking into components does not impact how much code runs on updates.&lt;/p&gt;

&lt;h2&gt;
  
  
  Add memoization
&lt;/h2&gt;

&lt;p&gt;The final code can be seen under (&lt;code&gt;src/routes/add-memo/add-memo.tsx&lt;/code&gt;). Here is a list of changes made:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The table and line chart components are wrapped in &lt;code&gt;useMemo&lt;/code&gt; to ensure that they are not re-rendered when non-relevant changes cause re-rendering of the application.&lt;/li&gt;
&lt;li&gt;Moved inline constants outside the function component. This ensures that we are not creating new but equivalent objects on re-render as these might cause re-render of the children.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;React is planning on moving to a &lt;a href="https://react.dev/blog/2024/02/15/react-labs-what-we-have-been-working-on-february-2024" rel="noopener noreferrer"&gt;new compiler&lt;/a&gt; which can do some of this for you. But right now, this responsibility falls on the developer.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;Let us look at the profiling plots again:&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%2Fm8umb2kk25qnhp34i31v.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%2Fm8umb2kk25qnhp34i31v.png" alt="Render Count" width="800" height="420"&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%2Fyymm6trq6s8wlq0dqrha.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%2Fyymm6trq6s8wlq0dqrha.png" alt="Average render duration" width="800" height="420"&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%2Fne60sbmo2nycpe7j3j0t.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%2Fne60sbmo2nycpe7j3j0t.png" alt="Total time spent" width="800" height="429"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;There are some interesting trends this time: &lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The average render duration is reduced. It has become half from a maximum of &lt;code&gt;180ms&lt;/code&gt; to &lt;code&gt;90ms&lt;/code&gt;. This is because the table is not re-rendered on every alternate render.&lt;/li&gt;
&lt;li&gt;This is reflected in the JS thread being able to process renders without any issues up to &lt;code&gt;39s&lt;/code&gt; which is a huge improvement over the previous &lt;code&gt;18s&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Render count is sustained at &lt;code&gt;10&lt;/code&gt; up to &lt;code&gt;39s&lt;/code&gt; and then starts to fall.&lt;/li&gt;
&lt;li&gt;Total time spent increases up to &lt;code&gt;39s&lt;/code&gt; and then fluctuates around the previous range.
&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Based on these and the insights from the unoptimised version, we understand that the JS thread starts struggling when the total time spent reaches &lt;code&gt;500-600ms&lt;/code&gt;. In the long run, this version of the app will also run into the same issues as before. It will take longer but the app will ultimately become less responsive.&lt;/p&gt;

&lt;p&gt;Now let us look at the React Profiler.&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%2Fjqdsdhc0hjlooxzdfhod.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%2Fjqdsdhc0hjlooxzdfhod.png" alt="Table re-rendered" width="800" height="698"&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%2Fg45hd49wm9qf6evl97d3.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%2Fg45hd49wm9qf6evl97d3.png" alt="Chart re-rendered" width="800" height="708"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The change in trend is because every alternate render is smaller. However, the render duration for the table keeps on increasing.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding optimisation
&lt;/h3&gt;

&lt;p&gt;If stock events are being sent every &lt;code&gt;200ms&lt;/code&gt;, there should be &lt;code&gt;5&lt;/code&gt; renders every second. However, we see that the renders (when the app is capable of handling the load) are twice that number. Let us focus on reducing the number of renders further here. If you look at the code (&lt;code&gt;src/routes/add-memo/add-memo.tsx&lt;/code&gt;), you'll see that there is a &lt;code&gt;useEffect&lt;/code&gt; on &lt;code&gt;line 51&lt;/code&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%2Fj9layy7hw4je19o8uq65.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%2Fj9layy7hw4je19o8uq65.png" alt="useEffect causing re-renders" width="800" height="515"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;This &lt;code&gt;useEffect&lt;/code&gt; runs when the list of stock events is changed. It calculates the average of the last 50 entries and also scrolls the table to the bottom. However, &lt;code&gt;useEffect&lt;/code&gt; is meant for synchronising with external systems. This is important due to the timing of triggering &lt;code&gt;useEffect&lt;/code&gt;. It runs &lt;a href="https://react.dev/reference/react/useEffect#caveats" rel="noopener noreferrer"&gt;after rendering&lt;/a&gt;, in a deferred event. If it runs React-related code, there might be a &lt;a href="https://react.dev/learn/you-might-not-need-an-effect" rel="noopener noreferrer"&gt;better place&lt;/a&gt; to put that code. &lt;strong&gt;This is one of the many magical rules that developers must remember and is not strictly enforced by React's design&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;In previous examples, &lt;code&gt;stockEventList&lt;/code&gt; is updated causing a re-render. This triggers the &lt;code&gt;useEffect&lt;/code&gt; which updates averagePrices and causes another re-render. We can put the code under useEffect to the observer for stock events. Since multiple state updates are being made together, they will be batched into a single update.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-off
&lt;/h4&gt;

&lt;p&gt;This change will again introduce refactoring. Many problems with React code happen because components aren't updated regularly. They can grow too big and confusing, making them hard to manage. Components can be dynamic with &lt;code&gt;useState&lt;/code&gt; and &lt;code&gt;useEffect&lt;/code&gt; hooks and it can become easy to lose track of which code inside the component runs when. Some code can run again and again even when not needed due to multiple updates to the component's state.  If that code is run due to an unrelated state update, it causes a functionality bug. It defeats React's purpose of creating correct applications. Developers need to trace the code and describe dependencies for changes. React provides helpful rules which can be enforced by &lt;code&gt;IDEs&lt;/code&gt; and &lt;code&gt;linting&lt;/code&gt;. &lt;/p&gt;

&lt;p&gt;Therefore, you should always try to refactor React code while updating it to ensure that only the correct code runs for a minimal number of times. Currently, this responsibility lies with the developer and introduces extra complexity while writing code.&lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce renders
&lt;/h2&gt;

&lt;p&gt;To batch the state updates together, we move the code from &lt;code&gt;useEffect&lt;/code&gt; into the observer logic. The result can be found here (&lt;code&gt;src/routes/reduce-renders/reduce-renders.tsx&lt;/code&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%2Fvd5p28l2tzpbmklo8omw.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%2Fvd5p28l2tzpbmklo8omw.png" alt="Move code to observer" width="800" height="461"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Another change you might notice is &lt;code&gt;useStableCallback&lt;/code&gt; hook. It is a hook that creates a function that does not change in reference but always runs the latest code when invoked. It is inspired by class components. React is planning on bringing something similar with &lt;a href="https://react.dev/learn/separating-events-from-effects#declaring-an-effect-event" rel="noopener noreferrer"&gt;useEffectEvent&lt;/a&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;Both state variables are now updated together in a single render. This is because React has now batched these updates together.&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%2F8myezow7g3sxq3u9021j.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%2F8myezow7g3sxq3u9021j.png" alt="Hooks change together" width="800" height="688"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Let us look at the profiling trends again:&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%2Fox6jln12c9o8503wwcwl.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%2Fox6jln12c9o8503wwcwl.png" alt="Render Count" width="800" height="418"&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%2Flkxoqssw8z3qf801myb5.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%2Flkxoqssw8z3qf801myb5.png" alt="Render Duration" width="800" height="414"&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%2Fuur45v234t9ooa75sbdw.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%2Fuur45v234t9ooa75sbdw.png" alt="Total time spent" width="800" height="415"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The app behaves similarly to the previous version. Here are the trends:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The number of renders is lower (&lt;code&gt;5&lt;/code&gt; when the app can process all the updates) which means there are no extra renders. &lt;/li&gt;
&lt;li&gt;The average render duration is back to a maximum of &lt;code&gt;180ms&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;This is because the number of renders is halved but each render is almost twice as expensive. In the previous version, every alternate render was very small. However this time, every update causes the table to re-render. Therefore calculations on every render are the same as the unoptimised version.&lt;/li&gt;
&lt;li&gt; Total time spent reaches the &lt;code&gt;500ms-600ms&lt;/code&gt; range at around the same time mark as before (&lt;code&gt;39s&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Adding optimisation
&lt;/h3&gt;

&lt;p&gt;We have reduced the number of renders.  The app is better than before but it still becomes slower and unresponsive. The root cause is the increasing duration of renders. If you look at the React profiler, you'd realise this is because of the table 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%2F6qpzwzbs5c1226xd1hi4.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%2F6qpzwzbs5c1226xd1hi4.png" alt="Table re-renders all the rows" width="800" height="68"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The table renders all the rows. On every update, all the rows are re-rendered. As the number of entries increases, the amount of time spent re-rendering all the rows will also increase. The average price plot does not show this trend since we only show running the average of the last 50 entries. It internally uses Canvas which is very performant under rapid updates.&lt;/p&gt;

&lt;p&gt;If we limit the number of rows rendered to only show the visible items, we can limit the cost of re-rendering the table. Even if there are a large number of rows in the table, only the ones on the screen will be rendered. That can be done by &lt;a href="https://medium.com/@atulbanwar/list-virtualization-in-react-3db491346af4" rel="noopener noreferrer"&gt;virtualization&lt;/a&gt;. I will use &lt;a href="https://virtuoso.dev/" rel="noopener noreferrer"&gt;react-virtuoso&lt;/a&gt; for the app.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-off
&lt;/h4&gt;

&lt;p&gt;This change will make sure that the app doesn't slow down and become unresponsive. However, with virtualization, we sacrifice the smoothness of the scroll. Scrolling rapidly will result in blank flashes inside the table component. This will also happen when the list scrolls to the bottom on new stock events. We also don't render the rows out of view which can hide them from the browser search functionality.  &lt;/p&gt;

&lt;h2&gt;
  
  
  Reduce computation
&lt;/h2&gt;

&lt;p&gt;The next version of the app can be found under &lt;code&gt;src/routes/reduce-computation/components/reduce-computation.tsx&lt;/code&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%2Fn08zp9js3pf35gmm9fkg.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%2Fn08zp9js3pf35gmm9fkg.gif" alt="App with virtualization" width="760" height="427"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here are the changes made:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The code for the chart and the table is broken into smaller components. They can be separately memoized using &lt;code&gt;memo&lt;/code&gt;. &lt;/li&gt;
&lt;li&gt;Also logic for averages has now moved into the &lt;code&gt;Chart&lt;/code&gt; component. This prevents the table from being re-rendered if there are updates from the &lt;code&gt;Chart&lt;/code&gt; component. &lt;/li&gt;
&lt;li&gt;We have also eliminated &lt;code&gt;averagePrices&lt;/code&gt; state and simplified calculations for calculating averages. Earlier we looped over the last &lt;code&gt;50&lt;/code&gt; entries of the list. Now we create new entries from previous averages.&lt;/li&gt;
&lt;li&gt;Inside the table component, we have replaced &lt;code&gt;NextTable&lt;/code&gt; with &lt;code&gt;TableVirtuoso&lt;/code&gt;
&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;Here are the trends:&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%2Fkugx7d09pfgyi3xmxnpc.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%2Fkugx7d09pfgyi3xmxnpc.png" alt="Render Count" width="800" height="411"&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%2Fuhf6722rbc2iz88gj4rc.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%2Fuhf6722rbc2iz88gj4rc.png" alt="Render Duration" width="800" height="411"&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%2Fe2gwrnj0ff4hfeuba54s.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%2Fe2gwrnj0ff4hfeuba54s.png" alt="Total time spent" width="800" height="411"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The average render duration is much lower. It is between &lt;code&gt;0.8ms-1.5ms&lt;/code&gt; on average. It does not increase. There are fluctuations which look significant. However, these are just small variations which are magnified due to the small scale of render durations.&lt;/li&gt;
&lt;li&gt;Total time spent is also limited to &lt;code&gt;18ms-33ms&lt;/code&gt;. It does not increase with time.&lt;/li&gt;
&lt;li&gt;The number of renders is constant but much higher with a value of &lt;code&gt;18-20&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;To understand why the number of renders is higher, we can check the React profiler.&lt;br&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%2F3njlqs5mxitextp99vvj.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%2F3njlqs5mxitextp99vvj.png" alt="Profiler for virtuoso table" width="800" height="439"&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%2Fan17zz8y70fttw5oygu4.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%2Fan17zz8y70fttw5oygu4.png" alt="Which hook re-rendered" width="800" height="432"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;code&gt;Hook 2&lt;/code&gt; causes extra re-renders for the &lt;code&gt;Virtuoso Table&lt;/code&gt; component. This hook is responsible for tracking which rows are in the view. The component works by showing the visible rows on the screen and adding space above and below them to account for the sizing of the list. We scroll to end on every update which causes re-renders inside the &lt;code&gt;Virtuoso Table&lt;/code&gt; component.&lt;/p&gt;

&lt;p&gt;Here is what the performance analysis looks like:&lt;br&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%2Fgyjdnudj8cmbcaw304zw.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%2Fgyjdnudj8cmbcaw304zw.png" alt="Chrome performance profiling" width="800" height="1208"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;JS thread is occupied for &lt;code&gt;13.8%&lt;/code&gt; of the observation time which is much lower than the previous &lt;code&gt;92%&lt;/code&gt;. There are no dropped frames and the app stays responsive in the longer run.&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding optimisation
&lt;/h3&gt;

&lt;p&gt;The app still feels unusable. The updates are too rapid for any meaningful interaction. It would be better if we could process multiple stock events together are larger intervals. It would also reduce the load on the JS thread. Also scrolling to the bottom seems to be adding more problems than value to the app. It is very hard to focus on a list that keeps scrolling. It causes extra re-renders in the virtual table and also causes blank flashes while the list scrolls. It can be eliminated if the table shows the list in reverse order, with new entries added to the top. This is something you should discuss with your &lt;code&gt;UX&lt;/code&gt; team. &lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-off
&lt;/h4&gt;

&lt;p&gt;Slowing down the rate of updating UI by handling multiple updates together will provide a better &lt;code&gt;UX&lt;/code&gt; and performance. However, adding multiple events together will also introduce larger visual shifts. It also makes state management complex. There is another overhead of reversing the list while rendering the table. But with slower updates, this should not be significant.&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimise Updates
&lt;/h2&gt;

&lt;p&gt;At this point, it is a good step to introduce a &lt;a href="https://react.dev/learn/scaling-up-with-reducer-and-context" rel="noopener noreferrer"&gt;state management library&lt;/a&gt;. React provides &lt;a href="https://react.dev/learn/scaling-up-with-reducer-and-context" rel="noopener noreferrer"&gt;context and reducers&lt;/a&gt; for complex state management. Using them, you can expose your state everywhere in the application and update it as needed. But they have certain limitations:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Context can expose multiple values combined in an object. Even if one of those values changes, the context will cause a re-render everywhere it is being consumed.&lt;/li&gt;
&lt;li&gt;You have no control over how the updates are triggered. If there is a re-render in the context provider which changes the context value, then all consumers will re-render.&lt;/li&gt;
&lt;li&gt;The developer needs to do most of the heavy lifting around state management logic&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Introducing a state management library can help make this process smoother. Here's how:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;You can precisely specify the data from the entire state store in your consumer component. The consumer will only re-render when that specific value is changed. So you have fine-grained control over the scope of re-renders on updates.&lt;/li&gt;
&lt;li&gt;You can use transient updates. In this case, you can subscribe to a value from the store in a component but you can choose when updates should cause a re-render. This is very useful in applications where there are a lot of rapid updates to the state. It is possible because external stores usually operate outside React's scope and you can control how the &lt;a href="https://react.dev/reference/react/useSyncExternalStore" rel="noopener noreferrer"&gt;integration&lt;/a&gt; works with your components.&lt;/li&gt;
&lt;li&gt;State management libraries provide a systematic way of maintaining your state-related logic. This is useful when you're working in a large team.&lt;/li&gt;
&lt;li&gt;Most of the libraries come with dedicated developer tools. They also come with a plethora of community-maintained plugins which can make complex state manipulation very easy&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;For our case, points &lt;code&gt;1&lt;/code&gt; and &lt;code&gt;2&lt;/code&gt; can help us provide a better way of handling updates in our rapidly updating application. I have used &lt;a href="https://github.com/pmndrs/zustand" rel="noopener noreferrer"&gt;zustand&lt;/a&gt; for the application.&lt;/p&gt;

&lt;h3&gt;
  
  
  Understanding how the store is structured
&lt;/h3&gt;

&lt;p&gt;The code for the store can be found at &lt;code&gt;src/routes/optimise-updates/store/stock-store.ts&lt;/code&gt;. Here is how it works:&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%2Fudb44ah721crhipz535k.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%2Fudb44ah721crhipz535k.png" alt="Zustand Store" width="800" height="836"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The store maintains a list of Stock Event IDs. This list can be read in the table component.&lt;/li&gt;
&lt;li&gt;A record of Stock events indexed by their ID. Individual table rows can read entries from this record. This is also useful where entries can also be updated. In this case, only the relevant row component will re-render. In the previous version, the table component accepted a list of event objects as &lt;code&gt;prop&lt;/code&gt;. Changing a single entry would've caused a re-render of the table component.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;We also have a custom hook which leverages &lt;a href="https://github.com/pmndrs/zustand?tab=readme-ov-file#transient-updates-for-often-occurring-state-changes" rel="noopener noreferrer"&gt;transient updates&lt;/a&gt;. We throttle the updates instead of immediately re-rendering the component on state change. The code can be found under &lt;code&gt;src/routes/optimise-updates/hooks/use-throttled-store.ts&lt;/code&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%2Fpuae4dswbqe4ludoehs7.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%2Fpuae4dswbqe4ludoehs7.png" alt="Use throttled store" width="800" height="649"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Using this hook will make sure that updates are throttled to the specified interval. The default value is &lt;code&gt;600ms&lt;/code&gt; which means &lt;code&gt;3&lt;/code&gt; stock events related updates will trigger &lt;code&gt;1&lt;/code&gt; re-render.&lt;/p&gt;

&lt;p&gt;Now we can use this in the &lt;code&gt;chart&lt;/code&gt; and &lt;code&gt;table&lt;/code&gt; component.&lt;br&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%2Fj6f0b03h5lmy6cb1bdil.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%2Fj6f0b03h5lmy6cb1bdil.png" alt="Table with throttled reversed list" width="800" height="228"&gt;&lt;/a&gt;&lt;br&gt;
Note that list of IDs is reversed before rendering. This eliminates scrolling to bottom on every event. If needed, reversed list can also be maintained in the store.&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%2F7x8hbmhgbuuwsrn53gnb.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%2F7x8hbmhgbuuwsrn53gnb.png" alt="Chart Hook Usage" width="800" height="66"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;We read the stock event data in &lt;code&gt;StockTableRow&lt;/code&gt; component. That doesn't need throttling. This is because it gets the ID from the &lt;code&gt;table&lt;/code&gt; component which already gets throttled updates.&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%2F7wio5j0uqi1urhaspig2.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%2F7wio5j0uqi1urhaspig2.png" alt="Stock table row does not need throttling" width="800" height="62"&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%2F96mu39dedfx1ik79j9pd.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%2F96mu39dedfx1ik79j9pd.gif" alt="App with throttled updates" width="800" height="450"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The final code can be found at &lt;code&gt;src/routes/optimise-updates/components/optimise-updates.tsx&lt;/code&gt;. &lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling
&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%2Fpxkhhjb8nqtsj64ffv5n.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%2Fpxkhhjb8nqtsj64ffv5n.png" alt="Render Count" width="800" height="412"&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%2Ftgmafcnbtnn47xqc8w0a.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%2Ftgmafcnbtnn47xqc8w0a.png" alt="Render Duration" width="800" height="420"&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%2Fmfmnb1tpo5giu6g8sh64.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%2Fmfmnb1tpo5giu6g8sh64.png" alt="Total time spent" width="800" height="409"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Initially, there was a spike in render count and total time spent. That is due to the virtual table calculating layout as entries cause overflow. However, that is not seen once the scroll has been shown in the table. To avoid the spike, can rely on the fact that all the rows are of the same height. We can ask virtuoso to skip this calculation by &lt;a href="https://virtuoso.dev/virtuoso-api/interfaces/VirtuosoProps/#fixeditemheight" rel="noopener noreferrer"&gt;providing the size in props&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;After the initial spike:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Render count stays between &lt;code&gt;1-3&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Render duration stays between &lt;code&gt;0.1-0.7ms&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Total time spent stays between &lt;code&gt;0.5-2ms&lt;/code&gt;. &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This is a huge optimisation over the previous version where the total time spent maxed to &lt;code&gt;33ms&lt;/code&gt;. Performance monitor further shows that UI is less rapidly updated now. JS thread only spends &lt;code&gt;5.38%&lt;/code&gt; time on rendering effort.&lt;br&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%2Fzhn84wxanxllusexfl8z.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%2Fzhn84wxanxllusexfl8z.png" alt="Performance with optimised updates" width="800" height="938"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Adding optimisation
&lt;/h3&gt;

&lt;p&gt;Now the application seems to be stable. However, if you think about the store, it will grow indefinitely in the long run. That will cause memory usage to grow. We can remove old entries from the store to free up memory. In a real-world use case, you'd refetch those entries when needed again. This will make sure that memory does not increase unbounded.&lt;/p&gt;

&lt;h4&gt;
  
  
  Trade-off
&lt;/h4&gt;

&lt;p&gt;This will add extra complexity to state updates. Processing updates where older entries are deleted might take longer&lt;/p&gt;

&lt;h2&gt;
  
  
  Optimise Memory
&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%2Fseh1jtfvqsbdajogckqg.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%2Fseh1jtfvqsbdajogckqg.png" alt="Limit memory usage" width="800" height="251"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The updated store can be found here: &lt;code&gt;src/routes/optimise-memory/store/stock-store.ts&lt;/code&gt;. After the size of the ID list reaches &lt;code&gt;200&lt;/code&gt;, we remove the first &lt;code&gt;100&lt;/code&gt; entries from the store. So only a maximum of &lt;code&gt;200&lt;/code&gt; entries is maintained.&lt;/p&gt;

&lt;h3&gt;
  
  
  Profiling
&lt;/h3&gt;

&lt;p&gt;Updated code can be found under &lt;code&gt;src/routes/optimise-memory/components/optimise-memory.tsx&lt;/code&gt;. Here is the performance analysis before the memory optimisation:&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%2Fikl6wrwwc53kio0p7nyq.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%2Fikl6wrwwc53kio0p7nyq.png" alt="Performance for optimised updates" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Here is the performance profile after optimisation:&lt;br&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%2F024pce9zgswqftvtgzpm.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%2F024pce9zgswqftvtgzpm.png" alt="Performance with memory optimisation" width="800" height="453"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The memory usage doesn't go down by much. Earlier it was in the range of &lt;code&gt;11.0-19.4 MB&lt;/code&gt;. Now it is in the range of &lt;code&gt;11.2-19.3 MB&lt;/code&gt;. This is probably because the event objects are too small and don't take much memory. It is also concerning to see a range of dropped frames in between. These are due to the store being cleared which blocks the JS thread.&lt;/p&gt;

&lt;h3&gt;
  
  
  What went wrong?
&lt;/h3&gt;

&lt;p&gt;This is an example of over-optimisation. We could save some memory usage but the cost of computation was higher. The version without memory usage optimisation is good enough since there isn't a practical use case where someone would monitor the stocks for so long that their memory is filled up.&lt;/p&gt;

&lt;p&gt;But if we want to ensure that their memory does not increase unbounded, we can experiment with the point at which the store is cleared and the number of entries removed at once. Tweaking these parameters should get us into a zone of acceptable performance.&lt;/p&gt;

&lt;h2&gt;
  
  
  Summing up
&lt;/h2&gt;

&lt;p&gt;Optimisation is a tricky process. You need to observe your application, correctly debug the bottlenecks and mitigate those with good design. There are added complications due to React's declarative model since you cannot completely control which code runs when. Depending on your use case, there might be other optimisations possible. Some of the common ones are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Bundle Splitting and Lazy Loading&lt;/strong&gt;: Splitting your built application into smaller chunks that can be lazily loaded. These chunks are only sent to the client application if they are needed. It helps reduce the amount of code loaded on the client for your application to work.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Server Side Rendering&lt;/strong&gt;: This is a hybrid of the older server rendered applications and newer single-page applications. You can selectively render your components on the server and serve them to the client readily. That helps increase security, abstract away logic from the client and provides better load times &amp;amp; search engine visibility.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Web Workers and Web Assembly&lt;/strong&gt;: For applications that are computation-heavy, you can offload the work to a separate thread. These are called web workers. You can also run compiled &lt;code&gt;C++&lt;/code&gt; (or other supported languages) code via Web assembly which gives you the benefit of high performance in the browser.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Did I miss something? Can I make this article better? Is there something that you liked reading? Let me know in the comments!&lt;/p&gt;

&lt;h4&gt;
  
  
  Credits
&lt;/h4&gt;

&lt;p&gt;Cover photo by RealToughCandy[dot]com: &lt;a href="https://www.pexels.com/photo/hand-holding-react-sticker-11035471/" rel="noopener noreferrer"&gt;https://www.pexels.com/photo/hand-holding-react-sticker-11035471/&lt;/a&gt;&lt;/p&gt;

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