# useController()

[Controller](https://dataclient.io/vue/api/Controller.md) provides type-safe methods to access and dispatch actions to the store.

For instance [fetch](https://dataclient.io/vue/api/Controller.md#fetch), [invalidate](https://dataclient.io/vue/api/Controller.md#invalidate),
and [setResponse](https://dataclient.io/vue/api/Controller.md#setResponse)

```html
<script setup lang="ts">
  import { useController } from '@data-client/vue';
  import { MyResource } from './resources';

  const props = defineProps<{ id: string }>();
  const ctrl = useController();

  const handleRefresh = async () => {
    await ctrl.fetch(MyResource.get, { id: props.id });
  };

  const handleSuspend = async () => {
    await ctrl.invalidate(MyResource.get, { id: props.id });
  };

  const handleLogout = () => {
    ctrl.resetEntireStore();
  };
</script>
```

`useController()` must be called inside `<script setup>` (or `setup()`), and requires the
[DataClientPlugin](https://dataclient.io/vue/api/DataClientPlugin.md) to be installed.
The same [Controller](https://dataclient.io/vue/api/Controller.md) is also available in templates and the Options API as [`$dataClient`](https://dataclient.io/vue/api/DataClientPlugin.md#dataclient).

## Examples

### Form submission

[fetch](https://dataclient.io/vue/api/Controller.md#fetch) returns the denormalized response, matching [useSuspense()](https://dataclient.io/vue/api/useSuspense.md)'s return type. This allows using Entity methods like `pk()`.

```html title="CreatePost.vue"
<script setup lang="ts">
  import { useController } from '@data-client/vue';
  import { useRouter } from 'vue-router';
  import { PostResource } from './PostResource';

  const ctrl = useController();
  const router = useRouter();

  const handleSubmit = async (e: Event) => {
    e.preventDefault();
    const post = await ctrl.fetch(
      PostResource.getList.push,
      new FormData(e.target as HTMLFormElement),
    );
    post.title;
    post.computedField;
    router.push(`/post/${post.pk()}`);
  };
</script>

<template>
  <form @submit="handleSubmit"><!-- fields --></form>
</template>
```

### Direct entity update

Use [set](https://dataclient.io/vue/api/Controller.md#set) for immediate updates without network requests. Supports functional updates to avoid race conditions.

```html title="VoteButton.vue"
<script setup lang="ts">
  import { useController } from '@data-client/vue';
  import { Article } from './Article';

  const props = defineProps<{ articleId: string }>();
  const ctrl = useController();

  const vote = () =>
    ctrl.set(Article, { id: props.articleId }, article => ({
      ...article,
      votes: article.votes + 1,
    }));
</script>

<template>
  <button @click="vote">Vote</button>
</template>
```

### Invalidate after mutation

Force refetch of related data using [invalidate](https://dataclient.io/vue/api/Controller.md#invalidate) or [expireAll](https://dataclient.io/vue/api/Controller.md#expireAll).

```html title="ClearUserCache.vue"
<script setup lang="ts">
  import { useController } from '@data-client/vue';
  import { UserResource } from './UserResource';

  const ctrl = useController();

  const handleClear = async () => {
    // invalidate() causes suspense; expireAll() refetches silently
    ctrl.expireAll(UserResource.get);
    ctrl.expireAll(UserResource.getList);
  };
</script>

<template>
  <button @click="handleClear">Refresh user data</button>
</template>
```

> **Tip**
>
> For better performance and consistency, prefer [including side effect updates in mutation responses](https://dataclient.io/rest/guides/side-effects.md).

### Prefetching

Use [fetchIfStale](https://dataclient.io/vue/api/Controller.md#fetchIfStale) to prefetch without overfetching fresh data.

```html title="ArticleLink.vue"
<script setup lang="ts">
  import { useController } from '@data-client/vue';
  import { ArticleResource } from './ArticleResource';

  const props = defineProps<{ id: string }>();
  const ctrl = useController();

  const prefetch = () =>
    ctrl.fetchIfStale(ArticleResource.get, { id: props.id });
</script>

<template>
  <RouterLink :to="`/article/${id}`" @mouseenter="prefetch">
    Read more
  </RouterLink>
</template>
```

### Websocket updates

Populate cache with external data via [set](https://dataclient.io/vue/api/Controller.md#set).

```ts title="useWebsocket.ts"
import { onMounted, onUnmounted } from 'vue';
import { useController } from '@data-client/vue';
import { EntityMap } from './resources';

export function useWebsocket(url: string) {
  const ctrl = useController();
  let ws: WebSocket;

  onMounted(() => {
    ws = new WebSocket(url);
    ws.onmessage = event => {
      const { entity, args, data } = JSON.parse(event.data);
      ctrl.set(EntityMap[entity], args, data);
    };
  });
  onUnmounted(() => ws?.close());
}
```

> **Warning**
>
> For production use, implement a [Manager for data streams](https://dataclient.io/vue/concepts/managers.md#data-stream) rather than component-level lifecycle hooks. Managers handle connection lifecycle globally and work with SSR.

### Todo App

Example app: [vue-todo-app](https://github.com/reactive/data-client/tree/master/examples/vue-todo-app) ([`src/resources/TodoResource.ts`](https://github.com/reactive/data-client/blob/master/examples/vue-todo-app/src/resources/TodoResource.ts), [`src/components/TodoItem.vue`](https://github.com/reactive/data-client/blob/master/examples/vue-todo-app/src/components/TodoItem.vue))
