# useSubscription()

Great for keeping resources up-to-date with frequent changes.

When using the default [polling subscriptions](https://dataclient.io/docs/api/PollingSubscription.md), frequency must be set in
[Endpoint](https://dataclient.io/rest/api/Endpoint.md), otherwise will have no effect.

> **Tip**
>
> [useLive()](https://dataclient.io/docs/api/useLive.md) is a terser way to use in combination with [useSuspense()](https://dataclient.io/docs/api/useSuspense.md),

## Usage

```typescript title="api/Price"
import { RestEndpoint, Entity } from '@data-client/rest';

export class Price extends Entity {
  symbol = '';
  price = '0.0';
  // ...

  pk() {
    return this.symbol;
  }
}

export const getPrice = new RestEndpoint({
  urlPrefix: 'http://test.com',
  path: '/price/:symbol',
  schema: Price,
  pollFrequency: 5000,
});
```

```tsx title="MasterPrice"
import { useSuspense, useSubscription } from '@data-client/react';
import { getPrice } from 'api/Price';

function MasterPrice({ symbol }: { symbol: string }) {
  const price = useSuspense(getPrice, { symbol });
  useSubscription(getPrice, { symbol });
  // ...
}
```

## 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 = useSubscription(TodoResource.get, id ? { id } : null);
> ```

> **Info: React Native**
>
> When using React Navigation, useSubscription() will sub/unsub with focus/unfocus respectively.

## Types

```typescript
function useSubscription(
  endpoint: ReadEndpoint,
  ...args: Parameters<typeof endpoint> | [null]
): void;
```

```typescript
function useSubscription<
  E extends EndpointInterface<
    FetchFunction,
    Schema | undefined,
    undefined
  >,
  Args extends readonly [...Parameters<E>] | readonly [null],
>(endpoint: E, ...args: Args): void;
```

## Examples

### Only subscribe while element is visible

```tsx title="MasterPrice.tsx"
import { useIntersectionObserver } from '@uidotdev/usehooks';
import { useSuspense, useSubscription } from '@data-client/react';
import { getPrice } from 'api/Price';

function MasterPrice({ symbol }: { symbol: string }) {
  const price = useSuspense(getPrice, { symbol });
  const [ref, entry] = useIntersectionObserver();
  // null params means don't subscribe
  useSubscription(getPrice, entry?.isIntersecting ? { symbol } : null);

  return <div ref={ref}>{price.price}</div>;
}
```

When `null` is sent as the second argument, the subscription is deactivated. Of course,
if other components are still subscribed the data updates will still be active.

[useIntersectionObserver()](https://usehooks.com/useintersectionobserver) uses [IntersectionObserver](https://developer.mozilla.org/en-US/docs/Web/API/Intersection_Observer_API), which is very performant. [ref](https://react.dev/reference/react/useRef) allows
us to access the [DOM](https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model).

### Crypto prices (websockets)

We implemented our own `StreamManager` to handle our custom websocket protocol. Here we listen to the [subscribe/unsubscribe
actions](https://dataclient.io/docs/api/Actions.md#subscribe) sent by `useSubscription` to ensure we only listen to updates for components that are rendered.

Example app: [coin-app](https://github.com/reactive/data-client/tree/master/examples/coin-app) ([`src/resources/StreamManager.ts`](https://github.com/reactive/data-client/blob/master/examples/coin-app/src/resources/StreamManager.ts), [`src/resources/Ticker.ts`](https://github.com/reactive/data-client/blob/master/examples/coin-app/src/resources/Ticker.ts), [`src/pages/Home/AssetPrice.tsx`](https://github.com/reactive/data-client/blob/master/examples/coin-app/src/pages/Home/AssetPrice.tsx))
