---
title: Messaging
---

At times you want to allow multiple features on a page to know about changes that happens in one component.
A common scenario is a list + detail scenario where you typically have a list and when you click an item you
get the details of it somewhere else on the page.

With React you would typically create a context and wrap the components needing the same information in
a React context. The problem with this is that you are having to create these different contexts, which creates
a set of boiler plate code that has little value and you need to put it into the correct place in the
rendering hierarchy. This creates a logical coupling in your application and is error prone when changing
structure.

With the MVVM model this doesn't really work either, as the contexts aren't available to the view model.
With publish/subscribe, components can use their view models to
publish messages that can be subscribed to by other parts of your system that lives on the page.

This creates for a more decoupled approach and making your code and structure easier to change.

## IMessenger

In Cratis Arc the system that deals with this is called `IMessenger`.
It has a method called `publish()` for publishing messages, and one for subscribing called `subscribe()`.
Both of these methods work on types and require a runtime type like a `class` to be the message type.
The implementation is provided by Arc core (`@cratis/arc/messaging`) and wired into MVVM bindings.

Below is an example of the view models for two components, one listing items and the other holding details.

These illustrative files assume an application-owned `User` model in `./User`. Define the shared message once in `UserSelected.ts` and import that same class in publisher and subscriber:

```ts
import { User } from './User';

export class UserSelected {
    constructor(readonly user: User) {}
}
```

Then for the list component we would typically have something like the following:

```ts
import { IMessenger } from '@cratis/arc.react.mvvm/messaging';
import { injectable } from 'tsyringe';
import { UserSelected } from './UserSelected';
import { User } from './User';

@injectable()
export class UsersListViewModel {
    // Take the IMessenger as a dependency
    constructor(private readonly _messenger: IMessenger) {}

    selectUser(user: User) {
        // Publish the message saying the user was selected
        this._messenger.publish(new UserSelected(user));
    }
}
```

Whenever a selection is done, the view would call the `selectUser()` method, which
would then publish the message.

For the details component, the view model would then need to subscribe to this:

```ts
import { IMessenger } from '@cratis/arc.react.mvvm/messaging';
import { injectable } from 'tsyringe';
import { Subscription } from 'rxjs';
import { UserSelected } from './UserSelected';
import { User } from './User';

@injectable()
export class UserDetailsViewModel {
    user?: User;
    private readonly _subscription: Subscription;

    constructor(messenger: IMessenger) {
        this._subscription = messenger.subscribe(UserSelected, (message) => {
            this.user = message.user;
        });
    }

    detached() {
        this._subscription.unsubscribe();
    }
}
```

With this, the details view model will subscribe to the `UserSelected` message and set the state of the
view model accordingly. The subscriber receives the **published message instance**, not its nested `user` payload. Retain the subscription and unsubscribe in `detached()` when the view model is disposed.

## Scoped messaging

When using Arc React, the root `<Arc />` provides a root messenger.
You can add nested scopes with `MessengerScope` from `@cratis/arc.react/messaging`.
View models resolved with `withViewModel()` receive the nearest scoped messenger automatically, and fall back to the root messenger.

By default:

- Messages do not bubble to parent scopes.
- Messages from parent scopes trickle down to child scopes recursively.

Override behavior through:

- `bubbleToParent`
- `trickleDownToChildren`
