Optimizing Blazor Code Episode 1 : using @key
In Blazor development, creating efficient and responsive user interfaces is essential. One handy feature you don’t want to overlook is the @key attribute. This article will guide you through how @key works, using a practical example of building a task management application to make the concepts clear and relatable
TaskMaster App
Imagine we’re building a Blazor application called TaskMaster. It’s a simple task management tool where users can add, reorder, and complete tasks. We’ll use this example to explore how the @key
attribute can significantly improve our app's performance and user experience
Let’s start with a basic implementation of our task list without using @key
:
@page "/"
<h1>TaskMaster</h1>
<ul class="task-list">
@foreach (var task in Tasks)
{
<li class="task-item @(task.IsCompleted ? "completed" : "")">
<span>@task.Title</span>
<button @onclick="() => ToggleTaskCompletion(task)">
@(task.IsCompleted ? "Undo" : "Complete")
</button>
</li>
}
</ul>
<button @onclick="AddTask">Add Random Task</button>
<button @onclick="ReorderTasks">Reorder Tasks</button>
@code {
List<Task> Tasks = new List<Task>
{
new Task { Id = 1, Title = "Learn Blazor", IsCompleted = false },
new Task { Id = 2, Title = "Build TaskMaster App", IsCompleted = false },
new Task { Id = 3, Title = "Master @key attribute", IsCompleted = false }
};
void AddTask()
{
var newId = Tasks.Count + 1;
Tasks.Add(new Task { Id = newId, Title = $"New Task {newId}", IsCompleted = false });
}
void ReorderTasks()
{
Tasks = Tasks.OrderBy(t => Guid.NewGuid()).ToList();
}
void ToggleTaskCompletion(Task task)
{
task.IsCompleted = !task.IsCompleted;
}
class Task
{
public int Id { get; set; }
public string Title { get; set; }
public bool IsCompleted { get; set; }
}
}
Inefficient Rendering
With this implementation, our TaskMaster app works, but it's not optimal. When we reorder tasks or add a new task, Blazor recreates all the task items in the DOM, even if only one item has changed. This can lead to performance issues, especially with a large number of tasks.
To visualize this, imagine each task item had a text input for notes. Every time we reorder the list, all input fields would be recreated, losing any unsaved changes and potentially causing a jarring user experience.
@key:
Now, let's modify our implementation to use the @key
attribute:
<ul class="task-list">
@foreach (var task in Tasks)
{
<li @key="task.Id" class="task-item @(task.IsCompleted ? "completed" : "")">
<span>@task.Title</span>
<input type="text" placeholder="Add notes..." />
<button @onclick="() => ToggleTaskCompletion(task)">
@(task.IsCompleted ? "Undo" : "Complete")
</button>
</li>
}
</ul>
By adding @key="task.Id"
, we're telling Blazor to track each task item by its unique ID.
How @key Works
- Unique Identification: Each task is now uniquely identified by its
Id
. - Efficient Updates: When we reorder tasks, Blazor can now track which task is which, moving the existing DOM elements instead of recreating them.
- State Preservation: The text input for notes will maintain its content even when the tasks are reordered
Benefits in Action
so @key
improves our TaskMaster app:
- Adding a Task:
- Without
@key
: All task items might be re-rendered. - With
@key
: Only the new task item is added to the DOM.
- Reordering Tasks:
- Without
@key
: All task items are recreated, losing any unsaved input. - With
@key
: DOM elements are moved, preserving all input states.
- Toggling Task Completion:
- Both with and without
@key
: Only the affected task is updated. - But with
@key
, we ensure consistent behavior if this action also involves reordering.
Performance Implications
In a small app like TaskMaster, the performance difference might not be noticeable. But imagine scaling this to hundreds or thousands of tasks:
- Without
@key
: Each reorder operation could cause significant DOM manipulation, potentially leading to noticeable lag. - With
@key
: Reordering becomes a smooth operation, with minimal DOM changes.
Best Practices :
- Use Unique, Stable Identifiers: We used
task.Id
, which doesn't change even if the task content does. - Avoid Index as Key: Notice we didn’t use the loop index. If we had, reordering would still cause unnecessary re-renders.
- Consistent Usage: We applied
@key
to all items in our task list for consistent behavior
Potential Issues :
While @key
is powerful, be aware of these potential issues:
- Non-unique Keys: If you accidentally use non-unique values for
@key
, you might see unexpected rendering behavior. - Changing Keys: If the key of an item changes (e.g., if we used a property that could be edited), Blazor would treat it as a new item, potentially causing unintended re-renders.
let’s dive into an advanced scenario :
Nested Components
Let’s evolve our TaskMaster app to use a separate TaskItem
component:
<ul class="task-list">
@foreach (var task in Tasks)
{
<TaskItem @key="task.Id" Task="task" OnToggleCompletion="ToggleTaskCompletion" />
}
</ul>
...
@code {
// ... (previous code remains the same)
}
// TaskItem.razor
<li class="task-item @(Task.IsCompleted ? "completed" : "")">
<span>@Task.Title</span>
<input type="text" placeholder="Add notes..." @bind="Notes" />
<button @onclick="OnToggleClicked">
@(Task.IsCompleted ? "Undo" : "Complete")
</button>
</li>
@code {
[Parameter] public Task Task { get; set; }
[Parameter] public EventCallback<Task> OnToggleCompletion { get; set; }
private string Notes { get; set; }
private void OnToggleClicked()
{
OnToggleCompletion.InvokeAsync(Task);
}
}
By using @key
with separate components, we ensure that each TaskItem
maintains its own state (like the notes input) even when the list is reordered
Conclusion:
Through our TaskMaster example, we’ve seen how the @key
attribute transforms a simple Blazor app into a more efficient and user-friendly experience:
- It enables smooth reordering of tasks without losing component state.
- It optimizes rendering performance, especially for larger lists.
- It preserves user input and component state during dynamic updates.
By mastering the @key
attribute, you're ready to create more responsive and efficient Blazor applications, providing a smoother experience for your users, whether you're building a simple task list or a complex, data-rich dashboard.
Remember, while @key
is a powerful tool, it's most effective when used judiciously and with a clear understanding of your application's structure and needs.
Happy coding!!