Sharing a Single Model Across Multiple Child Components in Blazor WebAssembly

There are several effective ways to share a single model (data object) between multiple child components in Blazor WebAssembly. Here are the best approaches:

1. Cascading Parameters (Best for hierarchical components)
<!-- ParentComponent.razor -->
@page "/parent"

<CascadingValue Value="@SharedModel">
    <ChildComponent1 />
    <ChildComponent2 />
</CascadingValue>

@code {
    private MyModel SharedModel { get; set; } = new MyModel();
}

<!-- ChildComponent1.razor -->
@code {
    [CascadingParameter]
    public MyModel SharedModel { get; set; }
}

<!-- ChildComponent2.razor -->
@code {
    [CascadingParameter]
    public MyModel SharedModel { get; set; }
}

2. Component Parameters (Best for direct parent-child relationships)

<!-- ParentComponent.razor -->
@page "/parent"

<ChildComponent1 Model="@SharedModel" />
<ChildComponent2 Model="@SharedModel" />

@code {
    private MyModel SharedModel { get; set; } = new MyModel();
}

<!-- ChildComponent1.razor -->
@code {
    [Parameter]
    public MyModel Model { get; set; }
}

<!-- ChildComponent2.razor -->
@code {
    [Parameter]
    public MyModel Model { get; set; }
}

3. State Management Service (Best for app-wide sharing)

// SharedModelService.cs
public class SharedModelService
{
    private MyModel _model = new();
    
    public MyModel Model 
    {
        get => _model;
        set
        {
            _model = value;
            NotifyStateChanged();
        }
    }
    
    public event Action OnChange;
    
    private void NotifyStateChanged() => OnChange?.Invoke();
}

Register the service in Program.cs:

builder.Services.AddSingleton<SharedModelService>();

Use in components:

@inject SharedModelService ModelService

@code {
    protected override void OnInitialized()
    {
        ModelService.OnChange += StateHasChanged;
    }
    
    private void UpdateModel()
    {
        ModelService.Model.Property = "New Value";
    }
}

4. EventCallback Pattern (For parent-child communication)

<!-- ParentComponent.razor -->
@page "/parent"

<ChildComponent1 Model="@SharedModel" ModelChanged="@HandleModelChanged" />
<ChildComponent2 Model="@SharedModel" ModelChanged="@HandleModelChanged" />

@code {
    private MyModel SharedModel { get; set; } = new();
    
    private void HandleModelChanged(MyModel updatedModel)
    {
        SharedModel = updatedModel;
        StateHasChanged(); // Refresh all components
    }
}

<!-- ChildComponent1.razor -->
@code {
    [Parameter]
    public MyModel Model { get; set; }
    
    [Parameter]
    public EventCallback<MyModel> ModelChanged { get; set; }
    
    private async Task UpdateModel()
    {
        Model.Property = "New Value";
        await ModelChanged.InvokeAsync(Model);
    }
}

5. Fluxor/Redux Pattern (For complex state management)

// Install package
dotnet add package Fluxor.Blazor.Web

// Define state
public record MyModelState
{
    public MyModel Model { get; init; } = new();
}

// Define actions
public record UpdateModelAction(MyModel Model);

// Create reducer
public static class Reducers
{
    [ReducerMethod]
    public static MyModelState ReduceUpdateModelAction(MyModelState state, UpdateModelAction action)
        => state with { Model = action.Model };
}

Use in components:

@inject IState<MyModelState> ModelState
@inject IDispatcher Dispatcher

<p>@ModelState.Value.Model.Property</p>

<button @onclick="UpdateModel">Update</button>

@code {
    private void UpdateModel()
    {
        var updatedModel = ModelState.Value.Model with { Property = "New Value" };
        Dispatcher.Dispatch(new UpdateModelAction(updatedModel));
    }
}

  1. For simple parent-child relationships: Use Component Parameters
  2. For deep component trees: Use Cascading Parameters
  3. For app-wide state: Use State Management Service or Fluxor
  4. For complex applications: Consider Fluxor/Redux pattern
  5. Immutable models: When sharing models, consider making them immutable or implementing proper change notifications

Performance Considerations

  • Avoid excessive re-rendering by implementing ShouldRender
  • Use [Parameter] public MyModel Model { get; set; } carefully as it can cause unnecessary renders
  • For large models, consider using view models or DTOs instead of full domain models
FavoriteLoadingAdd to favorites
Spread the love

Author: Shahzad Khan

Software developer / Architect

Leave a Reply