Lewati ke konten utama

Check .NET version (should be 8.0.x)

# Fix: Auto-Click Menu Issue di Blazor .NET 8

Home / Knowledge Base / Troubleshooting / Menu Auto-Click Fix


Problem

Setelah upgrade dari .NET 6 ke .NET 8, menu "mengklik sendiri" atau navigasi tidak stabil, terutama setelah ada notifikasi SignalR yang memicu StateHasChanged.

Root Cause

Technical Context

  • Framework: .NET 8 Blazor Server
  • UI Component: DevExpress DxTreeView
  • Issue: Enhanced navigation in .NET 8 changed rendering behavior

Specific Issues

  1. SignalR StateHasChanged - memicu re-render DxTreeView
  2. TreeView Animation (Slide) - menyebabkan selection event terpicu ulang
  3. Tidak ada navigation guard - double navigation terjadi
  4. Tidak ada debounce - rapid clicks tidak ter-handle
  5. .NET 8 Enhanced Navigation - lebih strict pada component lifecycle

Solutions Applied

1. Navigation Guard dengan Debounce (300ms)

Before:

void LoadPage(TreeViewNodeEventArgs e)
{
NavigationManager.NavigateTo(menuUrl);
}

After:

private bool isNavigating = false;
private string lastNavigatedUrl = string.Empty;
private System.Threading.Timer? navigationTimer;

void LoadPage(TreeViewNodeEventArgs e)
{
// Prevent duplicate navigation
if (isNavigating || targetUrl == lastNavigatedUrl)
return;

// Prevent navigation if already on the page
var currentUrl = NavigationManager.ToBaseRelativePath(NavigationManager.Uri);
if (currentUrl.Equals(targetUrl, StringComparison.OrdinalIgnoreCase))
return;

// Debounce navigation (300ms)
navigationTimer?.Dispose();
navigationTimer = new System.Threading.Timer(_ =>
{
InvokeAsync(() =>
{
NavigationManager.NavigateTo(targetUrl);
isNavigating = false;
});
}, null, 300, Timeout.Infinite);
}

2. Disable TreeView Animation

Before:

<DxTreeView AnimationType="LayoutAnimationType.Slide" ... >

After:

<DxTreeView AnimationType="LayoutAnimationType.None" ... >

3. Add Stable Rendering Key

Before:

<DxTreeView Data="@GlobalMenus" ... >

After:

<DxTreeView
Data="@GlobalMenus"
@key="@($"treeview-{GlobalMenus?.GetHashCode()}")" ... >

4. Implement IDisposable

@implements IDisposable

public void Dispose()
{
navigationTimer?.Dispose();
if (hubConnection is not null)
{
_ = hubConnection.DisposeAsync();
}
}

Files Modified

Project Structure

erp/
├── Neuron_ERP/ # Main Blazor Server application
│ └── Shared/
│ └── NavMenu.razor # ← Modified file
├── NeuronLibrary/ # Core library
├── NeuronLibraryUI/ # UI components library
├── docker-compose.yaml # Docker configuration
└── Dockerfile # Docker build file

Changes Applied

  • Neuron_ERP/Shared/NavMenu.razor
    • Added navigation guard
    • Added debounce timer (300ms)
    • Changed animation to None
    • Added @key for stable rendering
    • Implemented IDisposable

Testing Steps

Prerequisites

Verify your environment first:

# Check .NET version (should be 8.0.x)
dotnet --version

# Check project SDK
Get-Content Neuron_ERP/Neuron_ERP.csproj | Select-String "TargetFramework"

# Expected output: <TargetFramework>net8.0</TargetFramework>

1. Clean & Rebuild:

# Clean solution
dotnet clean

# Remove bin and obj folders
Get-ChildItem -Path . -Include bin,obj -Recurse -Directory | Remove-Item -Recurse -Force

# Restore and rebuild
dotnet restore
dotnet build

2. Test Scenarios:

  • Click menu - should navigate once
  • Click same menu twice - should ignore second click
  • SignalR notification arrives - menu should not auto-navigate
  • Click different menus rapidly - should debounce properly

3. Run Application:

# Run locally
dotnet run --project Neuron_ERP

# Or with Docker
docker-compose up --build

Browser Console Test

Open browser console (F12) and monitor for issues:

What to Check

// Blazor Server specific checks

// 1. Should not see duplicate navigation logs
// Look for: "Navigating to..." messages appearing twice

// 2. Should not see "Navigation in progress" errors
// Blazor will log if trying to navigate during another navigation

// 3. Should not see animation glitches
// TreeView should expand/collapse smoothly

// 4. Check SignalR connection
// Look for: "WebSocket connected" or "Long polling connected"

// 5. Monitor component lifecycle
// Enable verbose logging in browser DevTools > Console settings

Debugging in Visual Studio

// Add logging in NavMenu.razor.cs or NavMenu.razor @code block
protected override void OnAfterRender(bool firstRender)
{
base.OnAfterRender(firstRender);
Console.WriteLine($"NavMenu rendered. First: {firstRender}");
}

// Log navigation attempts
void LoadPage(TreeViewNodeEventArgs e)
{
Console.WriteLine($"Navigation requested to: {targetUrl}");
// ... rest of code
}

Enable Detailed Blazor Logging

Add to appsettings.Development.json:

{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning",
"Microsoft.AspNetCore.Components": "Debug",
"Microsoft.AspNetCore.SignalR": "Debug"
}
}
}

Expected Behavior After Fix

ScenarioBeforeAfter
Click menuSometimes double-navigatesSingle navigation
SignalR notificationAuto-clicks random menuNo auto-click
Rapid clicksAll clicks processedDebounced (300ms)
Same page clickNavigates againIgnored
AnimationJankySmooth (disabled)

Rollback (If Needed)

# Revert specific file
git checkout Neuron_ERP/Shared/NavMenu.razor

# Or revert all changes
git restore .

# Or reset to last commit
git reset --hard HEAD

.NET 8 Blazor Server Specific

  • Enhanced Navigation in .NET 8 Blazor
  • Blazor component re-rendering issues
  • SignalR StateHasChanged conflicts

DevExpress Components

  • DevExpress DxTreeView SelectionChanged event
  • TreeView animation conflicts with re-rendering

General Blazor Best Practices

  • Component lifecycle management
  • Navigation guards and debouncing
  • IDisposable implementation for cleanup

Environment

  • Framework: .NET 8
  • UI Framework: Blazor Server
  • Component Library: DevExpress Blazor
  • Database: PostgreSQL 17 (not related to this issue)
  • Real-time: SignalR

Performance Impact

  • Positive: Reduced unnecessary re-renders
  • Positive: Prevented double API calls from duplicate navigation
  • Positive: Improved UX with debounce
  • Negative: 300ms delay on navigation (barely noticeable)

Lessons Learned

  1. Always use debounce for navigation events
  2. Disable animations if causing re-render issues
  3. Use @key for stable component instances
  4. Implement IDisposable for cleanup
  5. Test with SignalR when using real-time updates

Created: Januari 2026
Version: 1.0