This article explores cascading values in Blazor 0.7.0
The source code for this article can be found here
This article is part of a Blazor state management exploration series.
- Blazor State Management Part I - Data-Binding
- Blazor State Management Part II - Event Delegation
- Blazor State Management Part III - Cascading Parameters
The last article ended with a question. How do we share data within the following component structure?
With event delegation, we could update middle components to accept a person
parameter and pass it through to child components that need it. We would also need to pass event handlers through the middle components. A doable, but a messy and cumbersome process.
Ideally, we would pass parameters from the parent component directly to child components that need them, no matter how nested the child components are. Well, it turns out, we can. CascadingValue is a unique tag that allows us to hoist parameters for child components, even nested ones, to acccess. This should feel familiar to anyone who has used React context.
Let’s create a MiddleComponent
to contain the DisplayPerson
and UpdatePerson
components.
@* MiddleComponent.cshtml *@
<div class="boxed">
<h4>DisplayPerson Component</h4>
<DisplayPerson></DisplayPerson3>
</div>
<div class="boxed">
<h4>UpdatePerson Componet</h4>
<UpdatePerson></UpdatePerson3>
</div>
Notice that we are no longer passing parameters to DisplayPerson
or UpdatePerson
. This is because the components will check the hoisted space for the values instead of receiving them directly as parameters. Update index.cshtml
to use the newly created MiddleComponent
.
@* index.cshtml *@
...
<MiddleComponent></MiddleComponent>
@functions {
protected Person person { get; set; } = new Person { Name = "Derek" };
...
}
This will not work. Remember, DisplayPerson
and UpdatePerson
need a Person
object to work with. So let’s wrap MiddleComponent
in a CascadingValue
component to hoist our person
object.
@* index.cshtml *@
...
<CascadingValue Value="@person">
<MiddleComponent></MiddleComponent>
</CascadingValue>
...
This is still not enough. UpdatePerson
also expects a function parameter. A function that will handle updating the state when necessary. Each CascadingValue
can only hoist one value so we will need to nest tags.
@* index.cshtml *@
<CascadingValue Value="@person">
<CascadingValue
Value="@HandleChange"
Name="HandleChange"
T="Action<UIChangeEventArgs>">
<MiddleComponent></MiddleComponent>
</CascadingValue>
</CascadingValue>
@functions {
protected Person person { get; set; } = new Person { Name = "Derek" };
protected void HandleChange (UIChangeEventArgs e)
{
person.Name = e.Value.ToString();
StateHasChanged();
}
}
The second CascadingValue
component is hoisting a named parameter. Since the parameter is a method, we need to help Blazor identify the parameter type by specifying the type with the T
parameter.
Next, we need to update UpdatePerson
and DisplayPerson
to use CascadingParameters
instead of normal parameters. We only need to update the decorators to specify CascadingParameters
. Nothing else needs to change.
@* UpdatePerson.cshtml *@
...
@functions {
[CascadingParameter]
protected Person person { get; set; }
[CascadingParameter(Name = "HandleChange")]
protected Action<UIChangeEventArgs> CustomOnChange { get; set; }
...
}
@* DisplayPerson.cshtml *@
...
@functions {
[CascadingParameter] protected Person person { get; set; }
}
Our application should work as it did at the end of the event delegation article. Updating the person’s name using the input field should update the name displayed in all components.
Conclusion
Yes, it is possible to use cascading parameters to share state throughout an application, but it is messy. The Blazor docs use CSS style rules or theme information as an example for using cascading parameters. read-only UI state or theme information are good candidates for cascading parameters. Otherwise, not only do the values need to be cascaded but so do the methods that will be responsible for updating the hoisted values. Not to mention, CascadingValue
can only hoist one value at a time, requiring nested tags for each cascading value.