Skip to main content

useCache()

Data rendering without the fetch.

General purpose store access can be useful when the data's existance is of interest (like if a user is authenticated), or general purpose store access like Query.

useCache() will rerender when its data mutates.

Usage

import { Entity, createResource } from '@data-client/rest';

export class User extends Entity {
  id = '';
  name = '';
  isAdmin = false;
  pk() {
    return this.id;
  }
  static key = 'User';
}
export const UserResource = createResource({
  path: '/users/:id',
  schema: User,
}).extend('current', {
  path: '/user',
  schema: User,
});
import { useLoading } from '@data-client/hooks';
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>
  );
}
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>
  );
}
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 />);
🔴 Live Preview
Store

See truthiness narrowing for more information about type handling

Behavior

Expiry StatusReturnsConditions
Invalidundefinednot in store, deletion, invalidation, invalidIfStale
Staledenormalized(first-render, arg change) & expiry < now
Validdenormalizedfetch completion
undefinednull used as second argument
Conditional Dependencies

Use null as the second argument on any reactive data client to indicate "do nothing."

// todo could be undefined if id is undefined
const todo = useCache(TodoResource.get, id ? { id } : null);

Types

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

Examples

Query arbitrary Entities

Query provides programmatic access to the Reactive Data Client store.

export class User extends Entity {
  id = '';
  name = '';
  isAdmin = false;
  pk() {
    return this.id;
  }
  static key = 'User';
}
export const UserResource = createResource({
  path: '/users/:id',
  schema: User,
});
import { Query, schema } from '@data-client/rest';
import { UserResource, User } from './UserResource';

const sortedUsers = new Query(
  new schema.All(User),
  (entries, { asc } = { asc: false }) => {
    const sorted = [...entries].sort((a, b) =>
      a.name.localeCompare(b.name),
    );
    if (asc) return sorted;
    return sorted.reverse();
  },
);

function UsersPage() {
  useFetch(UserResource.getList);
  const users = useCache(sortedUsers, { asc: true });
  if (!users) return <div>No users in cache yet</div>;
  return (
    <div>
      {users.map(user => (
        <div key={user.pk()}>{user.name}</div>
      ))}
    </div>
  );
}
render(<UsersPage />);
🔴 Live Preview
Store

Todo App

More Demos