Search code examples
.net-6.0blazor-server-sideevent-bubblingblazor-component

I need to update state of blazor component when other component changed


I have a blazor page with different components.

Component structure is the following:

page
   component1
      component11
    component2

When event happens on component11, component2 should update it's state.

On the same component I use OnStateChange event.

For update state of parent component, I can use EventCallback too.

But here I need to pass event two levels up and then down to other component.

Looks a little bit complex. Maybe exist some easy way to do this.

Can event bubbling work automatically? Maybe some application settings?


Solution

  • You need to use some form of notification pattern.

    Here's a simple provider. Note it requires the WeatherForecastService from DI to demo injecting services.

    public class TimeStampProvider
    {
        private readonly WeatherForecastService _weatherForecastService;
    
        public TimeStampProvider(WeatherForecastService weatherForecastService)
            => _weatherForecastService = weatherForecastService;
    
        public event EventHandler? TimeStampChanged;
    
        public string TimeStamp = string.Empty;
    
        public void NotifyTimeStampChanged(string timeStamp)
        {
            TimeStamp = timeStamp;
            this.TimeStampChanged?.Invoke(this, EventArgs.Empty);
        }
    }
    

    You can either register this as a scoped DI service or, if you're using it within the scope of a page create an instance in the page component and cascade it.

    Next a few components that use TimeStampProvider.

    TimeStampSetter

    <div class="m-2 p-2">
        <button class="btn btn-primary" @onclick=this.Clicked>Update Time Stamp</button>
    </div>
    
    @code {
        [CascadingParameter] private TimeStampProvider myProvider { get; set; } = default!;
    
        protected override void OnInitialized()
        {
            ArgumentNullException.ThrowIfNull(this.myProvider);
        }
        private void Clicked()
            => myProvider.NotifyTimeStampChanged(DateTime.Now.ToLongTimeString());
    }
    

    TimeStampViewer

    @implements IDisposable
    <div class="bg-dark text-white m-2 p-2">
        <pre>Stamp : @myProvider.TimeStamp </pre>
    </div>
    
    @code {
        [CascadingParameter] private TimeStampProvider myProvider { get; set; } = default!;
    
        protected override void OnInitialized()
        {
            ArgumentNullException.ThrowIfNull(this.myProvider);
            this.myProvider.TimeStampChanged += this.OnChanged;
        }
        private void OnChanged(object? sender, EventArgs e)
            => this.InvokeAsync(StateHasChanged);
    
        public void Dispose()
            => this.myProvider.TimeStampChanged -= this.OnChanged;
    }
    

    MyComponent

    <div class="bg-secondary m-3 p-2">
        <h3>MyComponent</h3>
        <TimeStampViewer />
        <TimeStampSetter />
    </div>
    @code {
    
    }
    

    And final the page, with lots of sub-components to keep in sync. Note the fixed cascading value.

    @page "/"
    @inject IServiceProvider ServiceProvider
    
    <PageTitle>Index</PageTitle>
    
    <h1>Hello, world!</h1>
    <CascadingValue Value=_myStateProvider IsFixed>
        <MyComponent />
        <TimeStampSetter />
        <TimeStampViewer />
        <TimeStampViewer />
        <TimeStampViewer />
        <TimeStampViewer />
        <TimeStampViewer />
        <TimeStampSetter />
    
    </CascadingValue>
    
    @code {
        private TimeStampProvider _myStateProvider = default!;
    
        protected override void OnInitialized()
        {
            // creates a new instance of TimeStampProvider in the context of the DI service container
            // which populates any DI services in the object
            _myStateProvider = ActivatorUtilities.CreateInstance<TimeStampProvider>(ServiceProvider);
    
            ArgumentNullException.ThrowIfNull(_myStateProvider);
        }
    }
    

    Test it and you'll see everything stays in sync!

    enter image description here