Explore MauiReactor and the MVU Pattern

Source: Telerik Blogs•

Explore MauiReactor and the MVU Pattern

Let’s check out the MauiReactor project and the Model-View-Update (MVU) pattern with a sample .NET MAUI app.

Let’s check out the MauiReactor project and the Model-View-Update (MVU) pattern with a sample .NET MAUI app.

When creating applications using .NET MAUI, we are used to working with content pages written in XAML, view models with binding properties and data models. However, today there is an alternative that lets us forget about bindings, with a more reactive approach like those on other platforms. In this article, we will explore the project MauiReactor and the MVU (Model-View-Update) pattern, and how you can use it in your own .NET MAUI projects. Let’s get started!

What Is the MVU Pattern?

MVU stands for Model-View-Update, which is a pattern that has been popularized in frameworks like React, Flutter, among others. The way each piece is defined is as follows:

  • Model: It is the state of your screen at a given moment. It is important not to confuse it with class models; rather it is everything needed for the UI to be drawn, for example a counter, a task list, etc.
  • View: This is a function that takes the current state and returns how the interface should look. It is important to know that when a change occurs in the state, the view is regenerated.
  • Update: This piece is fundamental in the pattern, as it is the only place where the state can be modified. When a user performs an action in the graphical interface, the state is updated, which causes the view to regenerate.

The pattern results in a unidirectional and predictable flow. Unlike MVVM, in MVU there are no two-way bindings or notifications via events. Instead, we could summarize it as a state, a function that renders that state, and a controlled way to update when it will be rendered.

What Is MauiReactor?

MauiReactor is a library hosted on GitHub, created by Adolfo Marinucci, which implements a component-based UI framework on top of .NET MAUI. This means you can write user interfaces based on the MVU pattern, using only declarative and fluent C# code.

The advantage MauiReactor offers is that Marinucci has created an equivalent for every .NET MAUI control. For example, this XAML code:

Would be translated to this in MauiReactor:

For now, don’t worry about the implementation; rather, I want you to notice how the interface is defined through the use of fluent methods, which makes the code readable. Also, there’s no XAML code or bindings.

Setting Up a .NET MAUI Project for MauiReactor

MauiReactor is distributed via the NuGet package Reactor.Maui, although it’s also possible to install a preconfigured template as indicated by the official repository. To do this, you should run the following command in the terminal:

Once the template has been created, we can create a new project using the following command:

One of the important steps for MauiReactor to work in a .NET MAUI project is that the traditional builder.UseMauiApp<App>() must be replaced by builder.UseMauiReactorApp<MainPage>(). This tells MauiReactor that the component MainPage will be the first screen shown.

Creating a Stateless Component in MauiReactor

Now that the project has been configured, let’s see how to create a component in MauiReactor. Let’s create a simple one that shows a message to the user, without any state. To do this, I will create a new class called MainPage.cs with the following content:

Let’s take a closer look at the code:

  • The class MainPage inherits from Component, which is the base class for every stateless component in MauiReactor.
  • The method Render is overridden, which returns the visual tree of the screen.
  • A page is created with the title Habit Tracker and a Label as content.
  • We use method chaining to configure the label properties.

When running the application, we will get a result like the following:

Now, let’s go a step further and implement state to react to user actions.

Creating a Stateful Component in MauiReactor

To be able to add state to a component in MauiReactor, we must do two things. First, define a class that represents the state, and, second, make the component inherit from Component<TState>. Let’s go back to our file MainPage.cs and modify it as follows:

Once again let’s analyze the class in detail:

  • We have defined a class called CounterState, with a single property Counter. This class will represent the state of the screen and is the Model part of the pattern.
  • We have changed the inheritance of MainPage, adding a generic of CounterState. This allows us to access the property called State, which gives us access to the component’s state.
  • The property State is used where we want the component’s information to be displayed. In our example we use an interpolated string to show the state of Counter in the Label. Here we see the View part of the MVU pattern.
  • The button receives an initial text to be shown in the UI to the user, as well as a callback that will be executed when pressed.
  • Inside the callback SetState(s => s.Counter++)) is invoked, representing the Update part of the pattern.

We have seen the different parts of the MVU pattern in action in the previous code. Now, what really happens behind the scenes?

Internally, when invoking SetState, the component is marked as invalid. Then the method Render is re-executed, which generates a new visual tree with the updated state. Finally, the new tree is compared with the previous one and only the differences are applied to the native controls. The result of the execution is the following:

Now that we have the basics of how MauiReactor works, let’s create a more useful application.

Creating a Habit-tracker Style App

We are going to create a simple application that allows us to display a series of habits with a name, a daily goal and a progress. To do this, let’s start by creating a new model called Habits:

Something interesting in the previous model is that the property IsCompleted is self-calculated based on whether the goal was reached or not, returning a true value when it is true.

Now, let’s update MainPage as follows:

Several interesting things happen in the previous code:

  • We have defined in MainPageState a list-type property of habits initialized as empty.
  • OnMounted is a lifecycle method of the component that MauiReactor invokes only once, when it is shown for the first time. Because of this, we use it to initialize the list with data.
  • In the visual part, we use a ScrollView so the list of habits can scroll if it grows.
  • We use .NET MAUI controls to define the graphical interface.

The previous code only defines a Label with the main title of the page, but it still does not display the list information. To achieve this, we are going to create a new smaller reusable component.

Creating a Reusable Component in MauiReactor

Although we could have created the whole UI from the main component, the ideal is always to have small blocks that we can reuse throughout the app. In this case, we will create a card-like component that receives a habit and shows a card with its name, progress and a button to increment it.

We will achieve this by creating a file HabitCard.cs:

The previous code shows some concepts that we have not yet explained, so I will take the opportunity to point them out to you:

  • The class is declared as partial, because MauiReactor uses a code generator to create fluent methods from our properties.
  • You can notice that we have used [Prop] attributes, which will allow the parent component to configure the properties.
  • A Action type property is used, which will allow the child component to notify the parent that a button was pressed, without the parent knowing what should happen next.
  • At some points in the app, UI values will be updated according to the habit’s state.

With this new component ready, let’s return to the main page to integrate it.

Using a Child Component in MauiReactor

The time has come to integrate the new child component into the main component. To achieve this, we will complete the Render method, creating a loop to iterate over each habit and display it through the new component:

In the previous code we do the following:

  • We calculate how many habits are complete to show to the user.
  • A list children of VisualNode nodes is created, which are visual nodes of MauiReactor. Each item will have a title and the progress of completed tasks.
  • We go through each habit and, for each of them, add the subcomponent HabitCard. Since we use the [Prop] attribute in the subcomponent’s properties, we can configure what will happen when its button is pressed.
  • In OnIncrement we call the SetState method, which triggers a new render. Both the subtitle and the card will update automatically.
  • We add a button with the text Reset day, which goes through all the habits and sets their count to zero.
  • We return the new content page with a vertical list that contains all the habits in the list.

Something we have seen in the implementation is that at no time are the controls manipulated directly. Rather, as the state changes, the entire interface is regenerated and MauiReactor applies only the differences.

When running the application, we will have the following result:

We can see that the result is a fully functional app, with an architecture completely different from what we are used to.

Conclusion

Throughout this article, we have explored what MauiReactor and the MVU pattern are, applied to .NET MAUI projects. We have seen that the pattern proposes an architecture different from the traditional MVVM, in which there are no bindings or notifications.

It is certainly an interesting proposal, although we must remember that it does not have official support from the .NET MAUI team. I invite you to explore the repository and experiment with your own projects.

What this article says