componentsblazor

How do I communicate between two sibling Blazor components?


I have a Blazor page with two components. One component has a button which generates a random number when clicked. The other component has a text area which should display the generated random number.

<h1>Parent Page</h1>

<ProvideNumberComponent />

<DisplayNumberComponent  />

@code {
}
<h3>Provides Number</h3>

<button class="btn btn-primary" @onclick="CalculateNumber">Provide Number</button>

@code {
    private void CalculateNumber(MouseEventArgs e)
    {
        Random rnd = new Random();
        Int32 nextNumber = rnd.Next();
    }

}
<h3>Displays number</h3>

<textarea cols="9" rows="1" readonly style="font-family:monospace;" />

@code {

}

What is the cleanest way to get the number from the calculate sibling component to appear in the display sibling component?

A problem with my code is that the Random object is instantiated on every button click, instead of once on initialization. Is this best addressed by placing the Random object in a singleton service class, and injecting that into the calculate component?


Solution

  • The best solution, to my mind, is to create a service which implements the state pattern and the notifier pattern. The following code describes how communication between two sibling can be done through an intermediary

    NotifierService.cs

    public class NotifierService
    {
        public NotifierService()
        {
    
        }
    
        int rnd;
        public int RandomNumber
        {
            get => rnd;
            set
            {
                if (rnd != value)
                {
                    rnd= value;
    
                    if (Notify != null)
                    {
                        Notify?.Invoke();
                    }
                }
            }
         }
         public event Func<Task> Notify;
     }
    

    Add this: services.AddScoped<NotifierService>();

    ProvideNumberComponent.razor

     @inject NotifierService Notifier
     @implements IDisposable
    
    <h3>Provides Number</h3>
    
     <button class="btn btn-primary" @onclick="CalculateNumber">Provide 
                                                        Number</button>
    
     @code 
     {
        private void CalculateNumber(MouseEventArgs e)
       {
          Random rnd = new Random();
          Int32 nextNumber = rnd.Next();
    
          Notifier.RandomNumber = nextNumber; 
       }
    
       public async Task OnNotify()
       {
        await InvokeAsync(() =>
        {
            StateHasChanged();
        });
      }
    
    
     protected override void OnInitialized()
     {
        Notifier.Notify += OnNotify;
     }
    
    
     public void Dispose()
     {
        Notifier.Notify -= OnNotify;
     }
    
    }
    

    DisplayNumberComponent.cs

     @inject NotifierService Notifier
     @implements IDisposable
    
     <hr />
    <h3>Displays number</h3>
    
    <textarea cols="9" rows="1" readonly style="font-family:monospace;">
        @Notifier.RandomNumber
    </textarea>
    
    @code {
    
        public async Task OnNotify()
       {
        await InvokeAsync(() =>
        {
            StateHasChanged();
        });
      }
    
    
     protected override void OnInitialized()
     {
        Notifier.Notify += OnNotify;
     }
    
    
     public void Dispose()
     {
        Notifier.Notify -= OnNotify;
     }
    
     }
    

    Of course you can inject and use the service in multiple components, as well as adding more features that the service can provide. Implementing communication by means of event handlers may be problematic, unless it is between a parent and its child...

    Hope this works...