This content originally appeared on DEV Community and was authored by Biswas Prasana Swain
When building modern web applications, efficiently updating the UI (user interface) is essential to keeping apps fast and responsive. A common strategy used in many frameworks (like React) is to use a Virtual DOM and components. This article will explain how components are rendered using a Virtual DOM and how we can optimize re-rendering so that the web app doesn’t become slow.
1. What is a Virtual DOM?
The DOM (Document Object Model) is a tree-like structure that represents all the elements on a webpage. Every time you interact with a webpage—clicking buttons, typing text—the browser has to update the DOM, which can be slow.
A Virtual DOM is like a copy of the real DOM but lives only in memory. Instead of updating the real DOM directly every time something changes, we update the Virtual DOM first. Once changes are made, the Virtual DOM compares itself to the old version, finds the differences (this is called diffing), and updates only the parts of the real DOM that need changing.
2. What are Components?
In a modern web app, components are the building blocks of the UI. Think of them as small, reusable parts of a webpage. For example:
- A button can be a component.
- A header can be a component.
- A list of items can be a component.
Each component describes what part of the UI should look like. A component function returns a Virtual DOM tree that represents that UI.
3. Example: Creating a Button Component
Let’s create a simple Button component using pseudocode. This component will return a button with text and a function that runs when the button is clicked.
// Component to display a button
function Button(props) {
// The Button component returns a Virtual DOM node for a <button> element
return new VirtualNode("button", { onClick: props.onClick }, [props.text])
}
In this example:
- The
Button
component takes in props (properties), like the text for the button and an event handler for when it’s clicked. - It returns a Virtual DOM node that represents a
<button>
element with the providedtext
andonClick
event handler.
4. Rendering Multiple Components
Let’s say we want to build an app that has a header and a button. Each of these parts can be represented as components. The app’s structure could look like this:
// App component with a header and button
function App() {
return new VirtualNode("div", {}, [
new Header(), // The Header component
new Button({ text: "Click Me", onClick: handleClick }) // The Button component
])
}
// Header component
function Header() {
return new VirtualNode("h1", {}, ["Welcome to the App!"])
}
// Function to handle button clicks
function handleClick() {
console.log("Button clicked!")
}
- The
App
component returns a Virtual DOM tree containing two components:Header
andButton
. - The
Header
component returns a Virtual DOM node representing an<h1>
element. - The
Button
component works as we described earlier.
5. How Initial Rendering Works
When the app runs for the first time, it:
-
Calls the components:
App()
,Header()
, andButton()
are executed. - Creates the Virtual DOM: The result is a tree of Virtual DOM nodes that represents the UI.
- Updates the real DOM: The Virtual DOM is used to build the actual HTML elements in the real DOM.
// Initial render of the app
function renderApp() {
let virtualDOM = App() // Render the app's Virtual DOM
let realDOM = createRealDOM(virtualDOM) // Convert the Virtual DOM into real DOM elements
attachToPage(realDOM) // Attach the real DOM elements to the webpage
}
6. Re-Rendering and Why We Need Optimization
Let’s say that something in the app changes, like the button text. Normally, the entire app would be re-rendered, but this can be slow if the app is large. Instead, we can optimize re-rendering by updating only the parts that changed.
Here’s what happens when re-rendering occurs:
- Diffing: We compare the old Virtual DOM with the new one and figure out what has changed.
- Patching: Only the parts of the real DOM that need to be updated are changed (this process is called patching).
Example: Changing the Button Text
Let’s say the button text changes from "Click Me" to "Clicked!". Here’s how we would re-render the button:
// New Button component with updated text
function Button(props) {
return new VirtualNode("button", { onClick: props.onClick }, [props.text])
}
// Re-rendering with the new text
let oldButton = Button({ text: "Click Me", onClick: handleClick })
let newButton = Button({ text: "Clicked!", onClick: handleClick })
// Diff the old and new Button
let diffResult = diff(oldButton, newButton)
// Patch the real DOM with the changes
patch(realButtonDOM, diffResult)
7. Optimizing Re-Rendering: Should Component Update
One of the key ways to optimize re-rendering is by checking whether a component actually needs to update. If nothing has changed in the component’s props or state, we can skip re-rendering that component. This is where the shouldComponentUpdate logic comes in.
// Function to check if a component should update
function shouldComponentUpdate(oldProps, newProps) {
return oldProps !== newProps // Only update if the props have changed
}
Now, before re-rendering, we check if the component should update:
// Example: Optimized re-rendering of Button component
function renderButtonIfNeeded(oldButton, newButton) {
if (shouldComponentUpdate(oldButton.props, newButton.props)) {
let realButton = createRealDOM(newButton)
patch(realButton)
}
}
8. Using Keys for List Optimization
When rendering lists of items (e.g., a list of buttons), we can optimize by using keys to uniquely identify each item. This helps the diffing algorithm match up old and new items in the list and apply only the necessary changes.
// List of buttons with unique keys
function ButtonList(items) {
return new VirtualNode("div", {}, items.map(item =>
new Button({ key: item.id, text: item.text, onClick: handleClick })
))
}
With keys, if one of the items in the list changes (like adding or removing a button), the algorithm can quickly identify which button changed and update only that one.
9. Optimizing State Changes
Components can also have their own state. When the state of a component changes, we only want to re-render that specific component, not the whole app. Here’s an example of a button with state:
// Button component with state
function ButtonWithState() {
let [clicked, setClicked] = useState(false) // Create state for button
function handleClick() {
setClicked(true) // Update state when clicked
}
return new VirtualNode("button", { onClick: handleClick }, [clicked ? "Clicked!" : "Click Me"])
}
In this case:
- The button text changes when it’s clicked.
- Only the
ButtonWithState
component re-renders, and the real DOM only updates the button text.
10. Avoid Re-Rendering Parent Components
Another optimization is to avoid re-rendering parent components when only a child component changes. For example, if the button changes but the Header
stays the same, we can skip re-rendering the Header
.
// Optimized App component
function App() {
if (!shouldComponentUpdate(oldHeaderProps, newHeaderProps)) {
return oldHeader // Reuse the old Header if it hasn't changed
}
return new VirtualNode("div", {}, [
new Header(), // Re-render the Header only if necessary
new ButtonWithState() // Button re-renders based on state
])
}
11. Conclusion: Efficient UI Updates with a Virtual DOM
To summarize, we can break down the process of rendering and optimizing components using the Virtual DOM into these steps:
- Initial Rendering: The first time the app is rendered, we build the Virtual DOM tree and convert it to the real DOM.
- Re-Rendering: When something changes (like the button text or state), we update the Virtual DOM and apply only the necessary changes to the real DOM.
-
Optimizing Re-Renders: By using strategies like
shouldComponentUpdate
, keys for lists, and state-based updates, we can avoid unnecessary re-rendering, keeping the app fast and responsive.
By thinking carefully about when and what to re-render, we can make sure that web applications remain efficient even as they grow in complexity. The Virtual DOM is a powerful tool that helps achieve this balance between simplicity and performance!
This content originally appeared on DEV Community and was authored by Biswas Prasana Swain
Biswas Prasana Swain | Sciencx (2024-09-26T16:31:02+00:00) How Components are Rendered in a Virtual DOM and How to Optimize Re-Rendering. Retrieved from https://www.scien.cx/2024/09/26/how-components-are-rendered-in-a-virtual-dom-and-how-to-optimize-re-rendering/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.