We are all connected to the Internet as neurons are connected to a giant brain.
~ Stephen Hawkings
The internet is becoming a necessity for everyone. As a result, there is an increased demand for video conferencing and optimized applications that are processing and data efficient. With this in mind, we at Huddle01 are constantly working to improve our video conferencing application while developing the deRTC Protocol.
We ran into a re-rendering issue while optimizing our Huddle01 video conferencing platform. This allowed us to deal with the problem of re-rendering with new solutions more efficiently.
Before diving into what re-rendering is, let’s talk about,
What is rendering 🤔
You may have heard of rendering from a friend who edits videos or creates crazy 3D art. Rendering has always been thought of as “processing” or a better synonym for “loading.”
Depending on the context, the word “rendering” can have several different meanings.
- If you are a video editor or a 3D artist, it refers to the process of generating images or a modal with computers.
- Depending on the context, it can mean to cause something to happen or to help someone.
In our case of web development, it means gathering different data and either processing or directly sending the output to the screen. Simply put, when you click on a website link, it renders as soon as you are directed to it.
What is re-rendering ⚙
We know that when a component loads on the screen for the first time, it’s called “rendering.” Similarly, whenever it’s loaded again, it’s called re-rendering.
Why does re-rendering happen 👀
When new information is added to the page, it needs to re-render some parts with further information. For example, when you press various buttons on the website, i.e., interact with it, there are some changes in it. When you press like on a tweet, the number count changes. The specific component which is displaying the like count gets updated.
Good application architecture refers to minimal re-renders as a part of the design. But re-renders are necessary as many applications have to be interactive.
There are two types of re-rendering.
Necessary re-render:
As the name implies, it refers to re-renders that are absolutely necessary. It also means that the re-rendered components are specific, with no effect on a larger scale than that. For example, when you toggle your microphone on and off in the Huddle01 app.
Unnecessary re-rendering:
As previously discussed, re-rendering is required for an interactive application. However, when unnecessary re-rendering occurs, not only does the specific component re-render, but all other elements that were not involved are also re-rendered. For example, when a keystroke causes the entire page to be re-rendered.
With this background, let’s see some of the re-rendering issues we faced and how we solved them!
What were the issues ❓
Whenever there is a UI update on the screen, a re-render is caused in order to update it for the user. Whenever a button is clicked, a re-render is caused to update the UI. The re-renders are denoted by the green border around the component which is being updated.
In our previous iteration of the meeting product (as shown below), there were a lot of re-renders on the UI, which triggered multiple side effects, causing memory leaks. The major reasons for this were:
Whenever a peer (or attendee) entered the meeting room, they got added to the list of peers (array of peers) which triggered a re-render to update the grid of peers to show them on the screen.
Whenever a speaker gets active, they get prioritized in the grid and the grid is supposed to be reshuffled which caused even more re-renders.
We used redux in the application and destructured properties from the state object using them in the dependency arrays of useEffects.
These re-renders over time made the app running in the browser slower and slower exponentially as the meeting progressed.
How did we fix it 🛠
To fix these issues, we made some major changes to our front-end architecture :
We memoized components using React.memo() wherever necessary, to prevent unnecessary re-rendering.
We provided unique keys to every peer viewport in the grid so that even if they shuffled in an array a re-render won’t be required to change their order. We also had to map the peer's array inside a separate child component to prevent re-renders.
We used a library called zustand instead of redux which is much easier to implement as well as has much more re-rendering optimization built-in.
Rather than destructuring the properties from the state object. We created new variables from the state selectors minimizing re-renders even more.
We minimized the use of useEffects for side effects and avoided putting variables in dependency arrays of useEffects.
Takeaways for you!
It may take some time, but if you try to optimize, you will eventually reach a solution that is both effective and intelligent. It took a significant amount of effort and time on our part to bring the app up to its current state of optimal performance. We stayed focused on our primary goal of improving the application and, over time, figured out ways to do so. This effort assisted aspiring developers in finding easy ways to optimize the re-rendering problem.
That’s all we have to say from our end! If you have any questions, suggestions, or team-ups in mind, reach out on Twitter or land on our discord!