---
sidebar_position: 7
title: Model Communication
---

# Model Communication

Model communication refers to communication between different Models, as well as communication between Effects and Actions within the same Model.

## Communication between Models

Models are not isolated from each other and can communicate with each other. There are mainly two scenarios:

1. Accessing the State and Actions of other Models in the Model.
2. Listening to changes in other Models in the Model.

Here, we will transform the simple counter application in the [Quick Start](/guides/topic-detail/model/quick-start) section into a counter application that allows you to set the step frequency. By setting the step frequency, you can affect the magnitude of each counter increase.

We abstract two Models, `stepModel` (step frequency) and `counterModel` (counter):

```ts
import { model } from '@modern-js/runtime/model';

const stepModel = model('step').define({
  state: 1,
});

const counterModel = model('count').define((context, { use, onMount }) => {
  const [, , subscribeStep] = use(stepModel);

  onMount(() => {
    return subscribeStep(() => {
      console.log(
        `Subscribe in counterModel: stepModel change to ${use(stepModel)[0]}`,
      );
    });
  });

  return {
    state: {
      value: 1,
    },
    actions: {
      add(state) {
        const step = use(stepModel)[0];
        state.value += step;
      },
    },
  };
});

export { stepModel, counterModel };
```

`stepModel` declares only one `state`, with an initial value of 1.

`counterModel` loads `stepModel` with the `use` function, and retrieves the returned `subscribeStep` function to listen for changes to the `stepModel` state. `onMount` is a hook function that is executed after the Model is mounted. `counterModel` begins to subscribe to the state changes of `stepModel` after it has been mounted, and prints out the latest value of `stepModel`.

`counterModel` accesses `stepModel` using the `use` function, and in the `add` function, the current value of `stepModel` (step frequency) can be obtained to perform increments using this value.

:::caution Note
When you need to access the state of another Model, you must call `use` during the actual execution phase of the current Actions or Effects function (in this example, the `add` function) to ensure that the obtained State is the latest value. Therefore, although we also call `use(stepModel)` in the callback function of `define`, we do not destructure the `state` value of `stepModel` because the callback function of `define` is executed during the mounting phase of the Model, and at this time, the `state` of `stepModel` obtained may be different from the value obtained when `add` is executed.
:::

Modify App.tsx:

```tsx
import { useModel } from '@modern-js/runtime/model';
import { counterModel, stepModel } from './models/count';

function Counter() {
  const [state, actions] = useModel(counterModel);
  const [step, stepActions] = useModel(stepModel);

  return (
    <div>
      <div>step: {step}</div>
      <button onClick={() => stepActions.setState(step + 1)}>add step</button>
      <div>counter: {state.value}</div>
      <button onClick={() => actions.add()}>add counter</button>
    </div>
  );
}

export default function App() {
  return <Counter />;
}
```

:::info Additional Information
Modern.js has enabled [auto-generate actions](./auto-actions.mdx) by default, so even though there are no Actions defined manually in the `stepModel`, the auto-generated `setState` can still be used.

:::

- Click **add step** to add steps.
- Click **add counter** to trigger the counter to increase.

The final effect is as follows:

![communicate-models](https://lf3-static.bytednsdoc.com/obj/eden-cn/aphqeh7uhohpquloj/modern-js/docs/models-communicate.gif)

:::info Additional Information
- Full example code for this section can be found [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/examples/runtime-api/models-communication).
- For more information about the relevant API, please refer to: [model](/apis/app/runtime/model/model_#function-types).

:::

In the previous example of `counterModel`, we called `use` within the Actions function to get other Model objects. If we only need to call Actions of other Models, we can also use `use` to get the Actions of Models in the `define` callback function because Actions are functions and there is no issue of value expiration. For example:

```ts
const barModel = model('bar').define({
  // ..
});

const fooModel = model('foo').define((context, utils) => {
  // get barModel actions
  const [, actions] = utils.use(barModel);
  return {
    // ...
    effects: {
      async loadA() {
        // ...
        // invoke barModel action
        barModel.actionA();
      },
      async loadB() {
        // ...
        // invoke barModel action
        barModel.actionB();
      },
    },
  };
});
```

Here, we no longer need to repeatedly get the `barModel` object in `loadA` and `loadB`, which simplifies the code logic.

## Communication within a Model

Communication within a Model can also be divided into two main scenarios:

1. Effects functions call the Actions functions of the same Model or other Effects functions.
2. Actions functions call other Actions functions of the same Model.

In the [Managing Side Effects](/guides/topic-detail/model/manage-effects) section, we demonstrated how Effects functions call Actions functions.

Here we provide another example:

```ts
const fooModel = model('foo').define((context, { use, onMount }) => ({
  state: {
    a: '',
    b: '',
  },
  actions: {
    setA(state, payload) {
      state.a = payload;
    },
    setB(state, payload) {
      state.a = payload;
    },
  },
  effects: {
    async loadA() {
      // get current Model actions
      const [, actions] = use(fooModel);
      const res = await mockFetchA();
      actions.setA(res);
    },
    async loadB() {
      // get current Model actions
      const [, actions] = use(fooModel);
      const res = await mockFetchB();
      actions.setB(res);
    },
  },
}));
```

In this example, the two Effects functions of `fooModel` need to call their own Actions functions. Here, we have called `use` once in each Effects function. Why can't we use `use` to get the Actions of the Model itself in the `define` callback function, as in the example of Model communication? This is because when calling `use` to get a Model, it first checks whether the Model has been mounted. If it has not been mounted, the mounting logic will be executed first.

The `define` callback function is executed during the mounting phase, so calling `use` to get the Model itself during the mounting phase will result in an infinite loop (which will throw an error in the actual execution process). Therefore, **you must not call `use` in the `define` callback function to get the Model itself**.

However, we can use the `onMount` hook function to get the Model itself through `use` after the Model has been mounted:

```ts
const fooModel = model('foo').define((context, { use, onMount }) => {
  let actions;

  onMount(() => {
    // after fooModel mounted, get actions
    [, actions] = use(fooModel);
  });

  return {
    state: {
      a: '',
      b: '',
    },
    actions: {
      setA(state, payload) {
        state.a = payload;
      },
      setB(state, payload) {
        state.a = payload;
      },
    },
    effects: {
      async loadA() {
        const res = await mockFetchA();
        actions.setA(res);
      },
      async loadB() {
        const res = await mockFetchB();
        actions.setB(res);
      },
    },
  };
});
```

In this way, we can also simplify the code.
