Skip to content

Add support for immutable classes #62570

Open
@mrpmorris

Description

@mrpmorris

Is there an existing issue for this?

  • I have searched the existing issues

Is your feature request related to a problem? Please describe the problem.

The ChangeDetection class will shortcut rerendering when it knows a Parameter value is an immutable type and the oldValue is equal to the newValue.

This is a request to be able to identify complex types (classes / structs) as immutable, so the ChangeDetection class can also skip rerendering if the type of the Parameter value is marked as immutable and the oldValue is the same reference as newValue.

Scenario

  1. Page has a List<Order>
  2. Page uses a grid component to render those rows
  3. Grid uses a component to render each row
  4. User clicks a button, edits a value, or performs any event that will cause the Page to rerender
  5. The parameter to the Grid is a reference type, so the grid will rerender
  6. Each Order in the list is a reference type, so each row in the grid will rerender

This problem becomes much more pronounced as we componentise a complicated page, having child components passing state to their own children etc, or have grids that have sub-grids (e.g. a grid of Order objects, where you can expand any row and see the OrderLine objects for that Order).

In many cases this is desirable, however, in cases where each row is an immutable object there is no need to rerender the row components at all.

Problem

In cases where the data is a view model only so will not change, this causes lots of unnecessary rerendering.

Describe the solution you'd like

Request

Change the Blazor ChangeDetection class so that if

  1. The oldValue of the parameter is the same reference as the newValue for the parameter,
  2. and newValue.GetType() is decorated as [Immutable],
  3. then assume the value is unchanged and therefore does not need to cause a rerender.

Common alternative suggestions

Override ShouldRender

This isn't a suitable solution because there are often times when developers are using 3rd party components, on which we cannot override this method.

We also don't want to override ShouldRender in the page, as we might want the page to rerender (e.g. changing a Date on an Order, but not changing any of the Lines that are presented in a grid).

Considerations

Non-breaking change

This should be a non-breaking change as it is up to the developer to specifically add [Immutable] to the models. If they do not wish to use this functionality, they do not need to add that attribute.

Ability to disable

In cases where the developer is unable to alter the data-model classes that do use this attribute (i.e. third party API) and they are experiencing a problem due to a 3rd party component vendor not yet accounting for this behaviour, then we could have a feature flag in Blazor that the ChangeDetection class checks. The developer can then disable this default behaviour and Blazor will act exactly as it already does.

No reliance on Blazor assembly

A clean architecture approach would mean not having references to UI assemblies such as Blazor from an API data-models (contracts) assembly. Therefore this [Immutable] attribute should be added to System.DataAnnotations.

It would not ensure the object is immutable, it would just be a hint to consumers of the class.

Conclusion

  • A non-breaking change that requires little work
  • that is possible to disable
  • that requires no work on the part of Blazor developers
  • that will work with components for which Blazor developers have no access to the source code
  • introduces a standard way of identifying immutability (and therefore no need to rerender) rather than expecting component venders to come up with their own solutions.

Additional context

References to similar requests / complaints on social media about rendering

https://www.reddit.com/r/Blazor/comments/sx8raj/prevent_bindvalue_rerendering_entire_page
https://www.reddit.com/r/Blazor/comments/ugi9qd/why_is_blazors_rendering_so_inefficient/
#38984
#13610
#40867
#21915
#26016

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-blazorIncludes: Blazor, Razor Components

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions