Skip to main content

v0.11 Queries, Queryable, and useQuery

· 9 min read
Nathaniel Tucker
Creator of Reactive Data Client

Besides the performance and data integrity benefits of normalizing the state, we get the added benefit of being able to safely access and query the store directly.

In this release we tune and simplify this functionality by unifying around the concepts of Querable Schemas. These include Entity, All, Collection, Query, and Union

The biggest impact of this change is the introduction of a new hook useQuery(), which allows direct store lookups using the Querable Schemas.

class User extends Entity {
username = '';
id = '';
groupId = '';
pk() {
return this.id;
}
static index = ['username' as const];
}

const bob = useQuery(User, { username: 'bob' });
const bob = useQuery(User, { id: '5' });

Similarly, we can lookup Querables with controller and snapshot using the controller.get

const bob = snapshot.get(User, { username: 'bob' });

Additionally, we have invested in further performance improvements, resulting in around a 2x performance increase for most operations and Queries being 16x faster.

Migration guide

Breaking Changes:

Other Highlights:

  • useCache() accepts Endpoints with sideEffects (like Resource.update)
  • Allow Entity.pk() to return numbers.

v0.2 Controller.fetch, async getHeaders, Collections

· 5 min read
Nathaniel Tucker
Creator of Reactive Data Client

Collections enable Arrays and Objects to be easily extended by pushing or unshifting new members. The namesake comes from Backbone Collections.

Collections are now the default schema for Resource.getList.

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

export class Todo extends Entity {
  id = 0;
  userId = 0;
  title = '';
  completed = false;
  pk() {
    return `${this.id}`;
  }
  static key = 'Todo';
}
export const TodoResource = createResource({
  urlPrefix: 'https://jsonplaceholder.typicode.com',
  path: '/todos/:id',
  searchParams: {} as { userId?: string | number } | undefined,
  schema: Todo,
  optimistic: true,
});
import { useController } from '@data-client/react';
import { TodoResource, type Todo } from './TodoResource';

export default function TodoItem({ todo }: { todo: Todo }) {
  const ctrl = useController();
  const handleChange = e =>
    ctrl.fetch(
      TodoResource.partialUpdate,
      { id: todo.id },
      { completed: e.currentTarget.checked },
    );
  const handleDelete = () =>
    ctrl.fetch(TodoResource.delete, {
      id: todo.id,
    });
  return (
    <div className="listItem nogap">
      <label>
        <input
          type="checkbox"
          checked={todo.completed}
          onChange={handleChange}
        />
        {todo.completed ? <strike>{todo.title}</strike> : todo.title}
      </label>
      <CancelButton onClick={handleDelete} />
    </div>
  );
}
import { useController } from '@data-client/react';
import { TodoResource } from './TodoResource';

export default function CreateTodo({ userId }: { userId: number }) {
  const ctrl = useController();
  const handleKeyDown = async e => {
    if (e.key === 'Enter') {
      ctrl.fetch(TodoResource.getList.push, {
        userId,
        title: e.currentTarget.value,
        id: Math.random(),
      });
      e.currentTarget.value = '';
    }
  };
  return (
    <div className="listItem nogap">
      <label>
        <input type="checkbox" name="new" checked={false} disabled />
        <input type="text" onKeyDown={handleKeyDown} />
      </label>
    </div>
  );
}
import { useSuspense } from '@data-client/react';
import { TodoResource } from './TodoResource';
import TodoItem from './TodoItem';
import CreateTodo from './CreateTodo';

function TodoList() {
  const userId = 1;
  const todos = useSuspense(TodoResource.getList, { userId });
  return (
    <div>
      {todos.map(todo => (
        <TodoItem key={todo.pk()} todo={todo} />
      ))}
      <CreateTodo userId={userId} />
    </div>
  );
}
render(<TodoList />);
🔴 Live Preview
Store

Upgrading is quite simple, as @data-client/rest/next and @data-client/react/next were introduced to allow incremental adoption of the new APIs changed in this release. This makes the actual upgrade a simple import rename.

Other highlights include

For all details, keep reading: