Skip to content

reactive/data-client

Repository files navigation

Reactive Data Client

CircleCI Coverage Status Percentage of issues still open bundle size npm version PRs Welcome Chat

Define your async methods. Use them synchronously in React. Instantly mutate the data and automatically update all usages.

For REST, GraphQL, Websockets+SSE and more

🌎 Website

Installation

npm install --save @data-client/react @data-client/rest @data-client/test @data-client/hooks

For more details, see the Installation docs page.

Usage

class User extends Entity {
  id = '';
  username = '';

  pk() {
    return this.id;
  }
}

class Article extends Entity {
  id = '';
  title = '';
  body = '';
  author = User.fromJS();
  createdAt = Temporal.Instant.fromEpochSeconds(0);

  pk() {
    return this.id;
  }

  static schema = {
    author: User,
    createdAt: Temporal.Instant.from,
  };
}
const UserResource = createResource({
  path: '/users/:id',
  schema: User,
  optimistic: true,
});

const ArticleResource = createResource({
  path: '/articles/:id',
  schema: Article,
  searchParams: {} as { author?: string },
  optimistic: true,
  paginationField: 'cursor',
});

One line data binding

const article = useSuspense(ArticleResource.get, { id });
return (
  <article>
    <h2>
      {article.title} by {article.author.username}
    </h2>
    <p>{article.body}</p>
  </article>
);
const ctrl = useController();
return (
  <CreateProfileForm
    onSubmit={data => ctrl.fetch(UserResource.getList.push, { id }, data)}
  />
  <ProfileForm
    onSubmit={data => ctrl.fetch(UserResource.update, { id }, data)}
  />
  <button onClick={() => ctrl.fetch(UserResource.delete, { id })}>Delete</button>
);
const price = useLive(PriceResource.get, { symbol });
return price.value;
const ctrl = useController();
ctrl.expireAll(ArticleResource.getList);
ctrl.invalidate(ArticleResource.get, { id });
ctrl.invalidateAll(ArticleResource.getList);
ctrl.setResponse(ArticleResource.get, { id }, articleData);
ctrl.fetch(ArticleResource.get, { id });
const queryTotalVotes = new schema.Query(
  new schema.All(Post),
  (posts, { userId } = {}) => {
    if (userId !== undefined)
      posts = posts.filter(post => post.userId === userId);
    return posts.reduce((total, post) => total + post.votes, 0);
  },
);

const totalVotes = useQuery(queryTotalVotes);
const totalVotesForUser = useQuery(queryTotalVotes, { userId });
class LoggingManager implements Manager {
  getMiddleware = (): Middleware => controller => next => async action => {
    console.log('before', action, controller.getState());
    await next(action);
    console.log('after', action, controller.getState());
  };

  cleanup() {}
}
const fixtures = [
  {
    endpoint: ArticleResource.getList,
    args: [{ maxResults: 10 }] as const,
    response: [
      {
        id: '5',
        title: 'first post',
        body: 'have a merry christmas',
        author: { id: '10', username: 'bob' },
        createdAt: new Date(0).toISOString(),
      },
      {
        id: '532',
        title: 'second post',
        body: 'never again',
        author: { id: '10', username: 'bob' },
        createdAt: new Date(0).toISOString(),
      },
    ],
  },
  {
    endpoint: ArticleResource.update,
    response: ({ id }, body) => ({
      ...body,
      id,
    }),
  },
];

const Story = () => (
  <MockResolver fixtures={options[result]}>
    <ArticleList maxResults={10} />
  </MockResolver>
);

...all typed ...fast ...and consistent

For the small price of 9kb gziped.    🏁Get started now

Features

Examples

  • Todo: GitHub | Sandbox
  • Github: GitHub | Sandbox
  • NextJS: GitHub | Sandbox

API

Reactive Applications