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() is reactive to data mutations; rerendering only when necessary.

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 to any RDC hook means "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

Remaining Todo Query

Queries can also be used to compute aggregates

More Demos

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.

More Demos

Github Comment Authorization

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

More Demos