# useCache()

Data rendering without the fetch.

Access any [Endpoint](https://dataclient.io/rest/api/Endpoint.md)'s response. If the response does not exist, returns
`undefined`. This can be used to check for an `Endpoint's` existence like for authentication.

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

## Usage

```ts title="UserResource"
import { Entity, resource } from '@data-client/rest';

export class User extends Entity {
  id = '';
  name = '';
  isAdmin = false;

  static key = 'User';
}
export const UserResource = resource({
  path: '/users/:id',
  schema: User,
}).extend('current', {
  path: '/user',
  schema: User,
});
```

```tsx title="Unauthed"
import { useController, useLoading } from '@data-client/react';
import { UserResource } from './UserResource';

export default function Unauthed() {
  const ctrl = useController();
  const [handleLogin, loading] = useLoading(
    (e: any) => ctrl.fetch(UserResource.current),
    [],
  );
  return (
    <div>
      <p>Not authorized</p>
      {loading ? (
        'logging in...'
      ) : (
        <button onClick={handleLogin}>Login</button>
      )}
    </div>
  );
}
```

```tsx title="Authorized"
import { useController } from '@data-client/react';
import { User, UserResource } from './UserResource';

export default function Authorized({ user }: { user: User }) {
  const ctrl = useController();
  const handleLogout = (e: any) => ctrl.invalidate(UserResource.current);

  return (
    <div>
      <p>Welcome, {user.name}!</p>
      <button onClick={handleLogout}>Logout</button>
    </div>
  );
}
```

```tsx title="Entry"
import { useCache } from '@data-client/react';
import { UserResource } from './UserResource';
import Unauthed from './Unauthed';
import Authorized from './Authorized';

function AuthorizedPage() {
  // currentUser as User | undefined
  const currentUser = useCache(UserResource.current);
  // user is not logged in
  if (!currentUser) return <Unauthed />;
  // currentUser as User (typeguarded)
  return <Authorized user={currentUser} />;
}
render(<AuthorizedPage />);
```

See [truthiness narrowing](https://www.typescriptlang.org/docs/handbook/2/narrowing.html#truthiness-narrowing) for
more information about type handling

## Behavior

| Expiry Status | Returns      | Conditions                                                                                                                                                                                                                                          |
| ------------- | ------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Invalid       | `undefined`  | not in store, [deletion](https://dataclient.io/rest/api/resource.md#delete), [invalidation](https://dataclient.io/docs/api/Controller.md#invalidate), [invalidIfStale](https://dataclient.io/docs/concepts/expiry-policy.md#endpointinvalidifstale) |
| Stale         | denormalized | (first-render, arg change) & [expiry < now](https://dataclient.io/docs/concepts/expiry-policy.md)                                                                                                                                                   |
| Valid         | denormalized | fetch completion                                                                                                                                                                                                                                    |
|               | `undefined`  | `null` used as second argument                                                                                                                                                                                                                      |

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

## Types

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

```typescript
function useCache<
  E extends Pick<
    EndpointInterface<FetchFunction, Schema | undefined, undefined>,
    'key' | 'schema' | 'invalidIfStale'
  >,
  Args extends readonly [...Parameters<E['key']>] | readonly [null],
>(endpoint: E, ...args: Args): DenormalizeNullable<E['schema']>;
```

## Examples

### Github Navbar login/logout

Our current user only exists when we are authenticated. Thus we can `useCache(UserResource.current)`
to determine whether to show the login or logout navigation buttons.

Example app: [github-app](https://github.com/reactive/data-client/tree/master/examples/github-app) ([`src/resources/User.ts`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/resources/User.ts), [`src/navigation/NavBar.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/navigation/NavBar.tsx))

### Github Comment Authorization

Here we only show commenting form if the user is authenticated.

Example app: [github-app](https://github.com/reactive/data-client/tree/master/examples/github-app) ([`src/resources/User.ts`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/resources/User.ts), [`src/pages/IssueDetail/CreateComment.tsx`](https://github.com/reactive/data-client/blob/master/examples/github-app/src/pages/IssueDetail/CreateComment.tsx))
