# useLive()

Async rendering of remotely triggered data mutations.

[useSuspense()](https://dataclient.io/vue/api/useSuspense.md) + [useSubscription()](https://dataclient.io/vue/api/useSubscription.md) in one composable.

`useLive()` is reactive to data [mutations](https://dataclient.io/vue/getting-started/mutations.md); rerendering only when necessary.

## Usage

```typescript title="Ticker" {33}
import { Entity, RestEndpoint } from '@data-client/rest';
import { Temporal } from 'temporal-polyfill';

export class Ticker extends Entity {
  product_id = '';
  trade_id = 0;
  price = 0;
  size = '0';
  time = Temporal.Instant.fromEpochMilliseconds(0);
  bid = '0';
  ask = '0';
  volume = '';

  pk(): string {
    return this.product_id;
  }
  static key = 'Ticker';

  static schema = {
    price: Number,
    time: Temporal.Instant.from,
  };
}

export const getTicker = new RestEndpoint({
  urlPrefix: 'https://api.exchange.coinbase.com',
  path: '/products/:productId/ticker',
  schema: Ticker,
  process(value, { productId }) {
    value.product_id = productId;
    return value;
  },
  pollFrequency: 2000,
});
```

```html title="AssetPrice.vue"
<script setup lang="ts">
  import { computed } from 'vue';
  import { useLive } from '@data-client/vue';
  import { getTicker } from './Ticker';
  import NumberFlow from '@number-flow/vue';

  const props = defineProps<{ productId: string }>();
  const ticker = await useLive(getTicker, computed(() => ({
    productId: props.productId,
  })));
</script>

<template>
  <div style="text-align: center">
    {{ productId }}
    <NumberFlow
      :value="ticker.price"
      :format="{ style: 'currency', currency: 'USD' }"
    />
  </div>
</template>
```

Like [useSuspense()](https://dataclient.io/vue/api/useSuspense.md), `useLive()` returns a Promise, so it is used with `await` in
`<script setup>` and requires a [Suspense](https://vuejs.org/guide/built-ins/suspense.html) ancestor.
The subscription is removed automatically when the component unmounts.

## Behavior

> **Tip: Conditional Dependencies**
>
> Use `null` as the second argument to any Data Client hook means "do nothing."
>
> ```typescript
> // todo could be undefined if id is undefined
> const todo = useLive(
>   TodoResource.get,
>   computed(() => (id.value ? { id: id.value } : null)),
> );
> ```

## Types

```typescript
function useLive(
  endpoint: ReadEndpoint,
  ...args: MaybeRefsOrGetters<Parameters<typeof endpoint>> | [null]
): Promise<DeepReadonly<ComputedRef<Denormalize<typeof endpoint.schema>>>>;
```

Arguments can be plain values, [refs](https://vuejs.org/api/reactivity-core.html#ref) (including [computed](https://vuejs.org/api/reactivity-core.html#computed)), or getter
functions like `() => ({ id: props.id })`. A plain object like `{ id: props.id }` is read once and won't
follow prop or route changes, so use a getter or `computed` when an argument can change.

The result updates (and the subscription is re-established) when the arguments change.
While data for new arguments loads, the result keeps the previous data instead of becoming `undefined`.
If that fetch fails, reading the result throws the error (per its [error policy](https://dataclient.io/vue/concepts/error-policy.md)), so it reaches
[onErrorCaptured()](https://vuejs.org/api/composition-api-lifecycle.html#onerrorcaptured).

## Examples

### Bitcoin Price (polling)

When our component with `useLive` is rendered, `getTicker` will fetch at [pollFrequency](https://dataclient.io/rest/api/RestEndpoint.md#pollfrequency)
milliseconds.
