dotnet / dotnet/aspnetcore

Add support for immutable classes

Open
#62,570 3 comments 10 reactions 0 assignees View on GitHub
area-blazor
Dominant language
C#
Stars
38.4k
Forks
10.9k
Avg merge
2d 6h
Merged PRs (30d)
290

Description

### Is there an existing issue for this?

- [x] 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`
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/
https://github.com/dotnet/aspnetcore/issues/38984
https://github.com/dotnet/aspnetcore/issues/13610
https://github.com/dotnet/aspnetcore/issues/40867
https://github.com/dotnet/aspnetcore/issues/21915
https://github.com/dotnet/aspnetcore/issues/26016

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.