Optimizing Blazor Code Episode 1 : using @key

Is It Vritra - SDE I
4 min readJul 15, 2024

--

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

  1. Unique Identification: Each task is now uniquely identified by its Id.
  2. Efficient Updates: When we reorder tasks, Blazor can now track which task is which, moving the existing DOM elements instead of recreating them.
  3. 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:

  1. Adding a Task:
  • Without @key: All task items might be re-rendered.
  • With @key: Only the new task item is added to the DOM.
  1. Reordering Tasks:
  • Without @key: All task items are recreated, losing any unsaved input.
  • With @key: DOM elements are moved, preserving all input states.
  1. 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 :

  1. Use Unique, Stable Identifiers: We used task.Id, which doesn't change even if the task content does.
  2. Avoid Index as Key: Notice we didn’t use the loop index. If we had, reordering would still cause unnecessary re-renders.
  3. 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:

  1. Non-unique Keys: If you accidentally use non-unique values for @key, you might see unexpected rendering behavior.
  2. 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:

  1. It enables smooth reordering of tasks without losing component state.
  2. It optimizes rendering performance, especially for larger lists.
  3. 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!!

--

--

Is It Vritra - SDE I
Is It Vritra - SDE I

Written by Is It Vritra - SDE I

Going on tech shits! AI is my pronouns

No responses yet