Important: This documentation covers Yarn 1 (Classic).
For Yarn 2+ docs and migration guide, see yarnpkg.com.

Package detail

@data-client/react

reactive2kApache-2.00.14.25TypeScript support: included

Async State Management without the Management. REST, GraphQL, SSE, Websockets, Fetch

react, data, cache, flux, suspense, fetch, hook, networking, async, concurrent mode, typescript, async, data fetching, data cache, reactive, state management, api client, api, normalized cache, swr, query, front-end, mobile, react-native, ios, android, web, expo, expogo, nextjs, middleware, websocket, REST, GraphQL, RPC, sse, declarative, dynamic data, mutations

readme

The scalable way to build applications with dynamic data.

Declarative resouce definitons for REST, GraphQL, Websockets+SSE and more
Performant rendering in React, NextJS, React Native, Expo

Schema driven. Zero updater functions.

Installation

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

For more details, see the Installation docs page.

Usage

Simple TypeScript definition

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

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

  static schema = {
    author: User,
    createdAt: Temporal.Instant.from,
  };
}

Create collection of API Endpoints

const UserResource = resource({
  path: '/users/:id',
  schema: User,
  optimistic: true,
});

const ArticleResource = resource({
  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>
);

Reactive Mutations

const ctrl = useController();
return (
  <>
    <CreateArticleForm
      onSubmit={article =>
        ctrl.fetch(ArticleResource.getList.push, { id }, article)
      }
    />
    <ProfileForm
      onSubmit={user =>
        ctrl.fetch(UserResource.update, { id: article.author.id }, user)
      }
    />
    <button onClick={() => ctrl.fetch(ArticleResource.delete, { id })}>
      Delete
    </button>
  </>
);

Subscriptions

const price = useLive(PriceResource.get, { symbol });
return price.value;

Type-safe Imperative Actions

const ctrl = useController();
await ctrl.fetch(ArticleResource.update, { id }, articleData);
await ctrl.fetchIfStale(ArticleResource.get, { id });
ctrl.expireAll(ArticleResource.getList);
ctrl.invalidate(ArticleResource.get, { id });
ctrl.invalidateAll(ArticleResource.getList);
ctrl.setResponse(ArticleResource.get, { id }, articleData);
ctrl.set(Article, { id }, articleData);

Programmatic queries

const queryTotalVotes = new schema.Query(
  new schema.Collection([BlogPost]),
  posts => posts.reduce((total, post) => total + post.votes, 0),
);

const totalVotes = useQuery(queryTotalVotes);
const totalVotesForUser = useQuery(queryTotalVotes, { userId });
const groupTodoByUser = new schema.Query(
  TodoResource.getList.schema,
  todos => Object.groupBy(todos, todo => todo.userId),
);
const todosByUser = useQuery(groupTodoByUser);

Powerful Middlewares

class LoggingManager implements Manager {
  middleware: Middleware = controller => next => async action => {
    console.log('before', action, controller.getState());
    await next(action);
    console.log('after', action, controller.getState());
  };

  cleanup() {}
}
class TickerStream implements Manager {
  middleware: Middleware = controller => {
    this.handleMsg = msg => {
      controller.set(Ticker, { id: msg.id }, msg);
    };
    return next => action => next(action);
  };

  init() {
    this.websocket = new WebSocket('wss://ws-feed.myexchange.com');
    this.websocket.onmessage = event => {
      const msg = JSON.parse(event.data);
      this.handleMsg(msg);
    };
  }
  cleanup() {
    this.websocket.close();
  }
}

Integrated data mocking

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

  • <input checked="" disabled="" type="checkbox"> TS Strong Typescript inference
  • <input checked="" disabled="" type="checkbox"> 🛌 React Suspense support
  • <input checked="" disabled="" type="checkbox"> 🧵 React 18 Concurrent mode compatible
  • <input checked="" disabled="" type="checkbox"> 💦 Partial Hydration Server Side Rendering
  • <input checked="" disabled="" type="checkbox"> 🎣 Declarative API
  • <input checked="" disabled="" type="checkbox"> 📝 Composition over configuration
  • <input checked="" disabled="" type="checkbox"> 💰 Normalized caching
  • <input checked="" disabled="" type="checkbox"> 💥 Tiny bundle footprint
  • <input checked="" disabled="" type="checkbox"> 🛑 Automatic overfetching elimination
  • <input checked="" disabled="" type="checkbox"> ✨ Fast optimistic updates
  • <input checked="" disabled="" type="checkbox"> 🧘 Flexible to fit any API design (one size fits all)
  • <input checked="" disabled="" type="checkbox"> 🔧 Debugging and inspection via browser extension
  • <input checked="" disabled="" type="checkbox"> 🌳 Tree-shakable (only use what you need)
  • <input checked="" disabled="" type="checkbox"> 🔁 Subscriptions
  • <input checked="" disabled="" type="checkbox"> ♻️ Optional redux integration
  • <input checked="" disabled="" type="checkbox"> 📙 Storybook mocking
  • <input checked="" disabled="" type="checkbox"> 📱 React Native support
  • <input checked="" disabled="" type="checkbox"> 📱 Expo support
  • <input checked="" disabled="" type="checkbox"> ⚛️ NextJS support
  • <input checked="" disabled="" type="checkbox"> 🚯 Declarative cache lifetime policy
  • <input checked="" disabled="" type="checkbox"> 🧅 Composable middlewares
  • <input checked="" disabled="" type="checkbox"> 💽 Global data consistency guarantees
  • <input checked="" disabled="" type="checkbox"> 🏇 Automatic race condition elimination
  • <input checked="" disabled="" type="checkbox"> 👯 Global referential equality guarantees

Examples

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

API