---
sidebar_position: 3
title: Use Models
---

# Use Models

## Using Models in Components

### Using as Global State

`useModel` can be used to obtain the State, Actions, and other information of the Model. When the State of the Model is modified by Actions, any other components that use the Model will automatically re-render.

In the counter example in [Quick Start](/guides/topic-detail/model/quick-start), we have demonstrated the use of `useModel` and will not repeat it here.

`useModel` supports passing multiple Models, and the State and Actions of multiple Models will be merged and returned as the result. For example:

```ts
const fooModel = model('foo').define({
  state: {
    value: 1,
  },
  actions: {
    add(state) {
      state += 1;
    },
  },
});

const barModel = model('bar').define({
  state: {
    title: 'bar',
  },
  actions: {
    set(state, payload) {
      state.title = payload;
    },
  },
});

const [state, actions] = useModel([fooModel, barModel]);
// 或
const [state, actions] = useModel(fooModel, barModel);
```

`state` and `actions` value are:

```ts
state = {
  value: 1,
  title: 'bar',
};

actions = {
  add(state) {
    state += 1;
  },
  set(state, payload) {
    state.title = payload;
  },
};
```

`useModel` also supports selector operations on State and Actions to filter or rename State and Actions. For example:

```ts
const fooModel = model('foo').define({
  state: {
    value: 1,
  },
  actions: {
    add(state) {
      state += 1;
    },
  },
});

const barModel = model('bar').define({
  state: {
    value: 'bar',
  },
  actions: {
    set(state, payload) {
      state.value = payload;
    },
  },
});

const [state, actions] = useModel(
  [fooModel, barModel],
  (fooState, barState) => ({
    fooValue: fooState.value,
    barValue: barState.value,
  }), // stateSelector
  (fooActions, barActions) => ({ add: fooActions.add }), // actionsSelector
);
```

We use `stateSelector` to rename the states with the same name in `fooModel` and `barModel`. We use `actionsSelector` to filter out the Actions of `barModel`.

If only `actionsSelector` needs to be set, you can set `stateSelector` to `undefined` as a placeholder. For example:

```ts
const [state, actions] = useModel(
  [fooModel, barModel],
  undefined,
  (fooActions, barActions) => ({ add: fooActions.add }), // actionsSelector
);
```

### Using as Static State

`useStaticModel` can be used to obtain the Model and use the state of the Model as a static state. This ensures that the State accessed by the component is always the latest value, but the change of the Model's State does not cause the current component to re-render.

:::info
The usage of `useStaticModel` is exactly the same as `useModel`.
:::

Consider the following scenario: there is an Input component responsible for user input, and another Search component responsible for executing a search operation after the user input information is entered and the search button is clicked. We do not want the state changes during the user input process to cause Search to re-render. In this case, `useStaticModel` can be used:

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

function Search() {
  // should not be destructured
  const [state] = useStaticModel(searchModel);

  return (
    <div>
      <button
        onClick={async () => {
          const result = await mockSearch(state.input);
          console.log(result);
        }}
      >
        Search
      </button>
    </div>
  );
}
```

:::warning Caution
Do not destructure the `state` returned by `useStaticModel`. For example, changing it to the following code:
`const [{input}] = useStaticModel(searchModel);`
will always get the initial value of Input.

:::

`useStaticModel` is also suitable for use with animation libraries such as [react-three-fiber](https://github.com/pmndrs/react-three-fiber), because binding fast-changing states in animation component UI can easily cause [performance issues](https://docs.pmnd.rs/react-three-fiber/advanced/pitfalls#never-bind-fast-state-reactive). In this case, you can choose to use `useStaticModel`, which only subscribes to the State but does not cause the view component to re-render. Here is a simplified example:

```ts
function ThreeComponent() {
  const [state, actions] = useStaticModel(modelA);

  useFrame(() => {
    state.value;
    actions.setValue(1);
    state.value;
  });
}
```

Using React's refs can also achieve similar effects. In fact, `useStaticModel` also uses refs internally. However, using `useStaticModel` directly helps decouple the state management logic from the component and converge it into the Model layer.

The complete sample code can be found [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/static-model).

### Using as Local State

`useLocalModel` can be used to obtain the Model and use the state of the Model as local state. At this time, the change of the Model State only causes the current component to re-render, but does not cause other components that use the Model to re-render. The effect is similar to managing state through `useState` in React, but it can decouple the state management logic from the component and converge it into the Model layer.

:::info
The usage of `useLocalModel` is exactly the same as `useModel`.
:::

For example, we modify the code of the counter application and add a counter component `LocalCounter` with local state:

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

function LocalCounter() {
  const [state, actions] = useLocalModel(countModel);

  return (
    <div>
      <div>local counter: {state.value}</div>
      <button onClick={() => actions.add()}>add</button>
    </div>
  );
}
```

Click the `add` button of `Counter` and `LocalCounter` respectively, and the states of the two do not affect each other:

![local-model](https://lf3-static.bytednsdoc.com/obj/eden-cn/eueh7vhojuh/modern/local-model.gif)

The complete sample code can be found [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/local-model).

## Using outside of components

In actual business scenarios, sometimes we need to use Model outside of React components, such as accessing State and executing Actions in utility functions. At this time, we need to use the Store. The Store is a low-level concept that users generally cannot touch. It is responsible for storing and managing the entire application's state. Reduck's Store is based on Redux's Store implementation and adds Reduck-specific APIs, such as `use`.

First, call `useStore` in the component to obtain the `store` object used by the current application and mount it to a variable outside the component:

```ts
let store; // Reference to `store` object outside of the component
function setStore(s) {
  store = s;
}
function getStore() {
  return store;
}

function Counter() {
  const [state] = useModel(countModel);
  const store = useStore();
  // Avoid unnecessary duplicate settings through `useMemo`
  useMemo(() => {
    setStore(store);
  }, [store]);

  return (
    <div>
      <div>counter: {state.value}</div>
    </div>
  );
}
```

You can obtain the Model object through `store.use`, and the usage of `store.use` is the same as `useModel`. Taking the counter application as an example, we perform an increment operation on the counter value every 1 second outside the component tree:

```ts
setInterval(() => {
  const store = getStore();
  const [, actions] = store.use(countModel);
  actions.add();
}, 1000);
```

The complete sample code can be found [here](https://github.com/web-infra-dev/modern-js-examples/tree/main/series/tutorials/runtime-api/model/counter-model-outof-react).

:::info
If the Store object is manually created through [`createStore`](/apis/app/runtime/model/create-store), there is no need to obtain it through `useStore` in the component, and it can be used directly.
:::

:::info Additional Information
For detailed API definitions related to this section, please refer to [here](/apis/app/runtime/model/model_).
:::
