{"id":17621493,"url":"https://github.com/sql-mistermagoo/blazoreventsdemo","last_synced_at":"2025-04-30T21:36:21.412Z","repository":{"id":91797693,"uuid":"194522383","full_name":"SQL-MisterMagoo/BlazorEventsDemo","owner":"SQL-MisterMagoo","description":"A Sample Blazor application highlighting the potential downside of using EventCallback","archived":false,"fork":false,"pushed_at":"2020-06-15T16:17:21.000Z","size":376,"stargazers_count":10,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-02-25T11:39:31.980Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/SQL-MisterMagoo.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2019-06-30T14:00:41.000Z","updated_at":"2023-06-22T17:30:47.000Z","dependencies_parsed_at":null,"dependency_job_id":"e950c6ae-2e7d-4132-a9f0-d9bd530f9f59","html_url":"https://github.com/SQL-MisterMagoo/BlazorEventsDemo","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorEventsDemo","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorEventsDemo/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorEventsDemo/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/SQL-MisterMagoo%2FBlazorEventsDemo/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/SQL-MisterMagoo","download_url":"https://codeload.github.com/SQL-MisterMagoo/BlazorEventsDemo/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":242710984,"owners_count":20173257,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-22T20:43:37.592Z","updated_at":"2025-03-09T15:30:42.451Z","avatar_url":"https://github.com/SQL-MisterMagoo.png","language":"C#","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Blazor Events\n\nA sample project that demostrates one way that EventCallback can hurt.\n\nHave a play - make yourself familiar with the possible performance impact of having the system always call StateHasChanged for you.\n\nBe informed - so you can decide what pattern is right for you.\n\n## Binding in blazor\n\nWhen you want to two-way bind a value to a blazor component, there are two required parameters.\n\nFor the purpose of this short introduction, we will call the bound parameter **\"Value\"**\n#### The Value parameter \n- Used to pass a reference from the Parent to the Child component\n- The child component can display and modify the Value \n\n  **Example**\n\n  `[Parameter] protected string Value { get; set; }`\n\n#### The ValueChanged parameter\n- For simple blazor two way binding (the child can read and modify the bound value) you must have a parameter that matches your Value parameter name and appends the word **Changed**\n- This parameter is used automatically by the default **`@bind-Value`** syntax as a way to receive notifications from the child component when it has modified the bound **Value**\n- There are two main ways to declare this parameter - using **`Action`** or using **`EventCallback`**\n- \n  **Example - EventCallback**\n\n  `[Parameter] protected EventCallback\u003cT\u003e ValueChanged { get; set; }`\n\n  **Example - Action**\n\n  `[Parameter] protected Action\u003cT\u003e ValueChanged { get; set; }`\n\n#### What's the difference?\n*I'll just mention one or two that I care about - there are other \ntechnical differences, but they are not relevant to this discussion.*\n\n##### For the developer:\n**EventCallback** can make life simpler - especially during \nprototyping as it automatically calls StateHasChanged on the \ntarget component (sometimes called the root Component) which \nwill cause a re-render of the parent and all of it's children.\n\n**Action** can make life more difficult - no automatic \n**StateHasChanged** is called so the developer needs to \nmanually call it when needed.\n\n##### For performance:\n**EventCallback** can cause many more calls to your component code - \nSetParameters, OnParametersSet/Async, ShouldRender, Markup rendering, \nOnAfterRender/Async etc. \n\nThis can double when your component performs async tasks as it will \nrender once before the async task and once after \n*(Turn on simulated workload in Sample 1 to see this)*\n\n*Note: The developer can prevent unwanted re-rendering in their own components by returning **`false`** in the **`ShouldRender`** override, but this can become difficult to manage as this method doesn't know which component/state change has triggered the refresh. See Sample 3.*\n\n**Action** can save you a lot of wasted cpu cycles - \nas it allows you to notify the parent/root component of a change \nin **state** without *forcing* a call to **StateHasChanged**. \n\nThis does mean your code needs to call **StateHasChanged** manually \nat the appropriate times, which can itself be complicated and, \nif done without care and attention, could end up causing just \nas many Re-renders as **EventCallback**.\n\n### Notes - Sample 1 - Balls\n\nThis page demonstrates a difference between Action and EventCallback in your value binding.\n\nEach Ball is bound to a value in the page - a simple array of integers - rendered in a `for loop`\n\nThe balls alternate between using Action and EventCallback - \nfor no good reason - don't let that distract you. It is just whimsy.\n\nOtherwise, the balls are identical.\n\nWhen a Ball is clicked, it increments it's bound **Value** and invokes the **ValueChanged** parameter.\n\nThe balls that use **EventCallback** will automatically trigger a \nfull re-render of the page, and the statistics will jump up in large numbers - \n*add more balls by typing a number into the text box to see those numbers really grow!*\n\nThe simulated workload is simply a Task.Delay with a random number of \nmilliseconds per button - the point of this is to emphasise the potential \nimpact on CPU that use of EventCallback in the wrong place can have.\n\n**Notice how you get twice as many updates with an async workload!**\n\n**Note: I have wrapped every component in a custom tag whose job is to flash \na yellow border - which itself forces an actual DOM update - without this, \nyou would not see a DOM update but the components would still render internally.\nThis is just an artifact to make the effect visible.**\n\n### Note - Sample 2 - Form Input\n\nThis page is lifted straight out of the blazor docs \nhttps://docs.microsoft.com/en-us/aspnet/core/blazor/forms-validation?view=aspnetcore-3.0\nand demonstrates the re-rendering effect of EventCallback in your \nvalue binding in a more *usual* page.\n\nAgain the yellow border flash is used to highlight when a re-render happens.\n\n### Note - Sample 3 - Form Input With Guard\n\nThis is the same page as Sample 2 but with an added guard in \nits **`ShouldRender`** method that prevents the full page \nre-rendering effect of **EventCallback**.\n\nAgain the yellow border flash is used to highlight when \na re-render happens, which should only occur when you submit the form.\n\n### What to do?\n\nIn my opinion, I have yet to find a right time and place for \n**EventCallback**, so my default is to use **Action**.\n\nI don't call **StateHasChanged** on a parent Component, unless I ***really*** \nwant to update every child.\n\nI believe you should separate your App State logic from your \nUI logic as much as possible, which means not having code automatically \ncalling **StateHasChanged**.\n\nIt doesn't matter what App State *pattern* you choose, but I \ndo believe you should choose one - there are awesome community developed \ncomponent libraries on awesome blazor (https://github.com/AdrienTorris/awesome-blazor) - \nI won't recommend a particular one - I haven't enough experience of them, \nbut the authors are definitely awesome!\n\nPlease don't treat your \"Views\" as State Containers - they are there to \nmanage rendering, not business logic.\n\nChoose when to update your UI - debate with yourself/your team about \nwhether components should handle their own UI state and decide when to refresh \nOR whether a parent component should decide when to refresh its children's UI.\n\nJust have the debate - make a choice - don't fall blindly into \nsomething that you didn't expect to be a problem.\n\nGot other examples? Or found a bug? =\u003e Pull Request!\n\n#### Try me\n\nhttps://blazorevents.azurewebsites.net/\nNote: hosted on the free tier of Azure - load times are slow!\n\nImagine if your view had hundreds of controls all internally re-rendering just because something happened in one of the components.\n\nThis is not neccessarily going to affect the DOM - the differ takes care of that - but do you want your server wasting cycles in Server side Blazor? \n\nDo you want your Client Side Blazor users complaining their browser is hogging CPU?\n\nMM","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsql-mistermagoo%2Fblazoreventsdemo","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsql-mistermagoo%2Fblazoreventsdemo","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsql-mistermagoo%2Fblazoreventsdemo/lists"}