npm.io
7.0.0 • Published 2 years ago

@rest-hooks/redux

Licence
Apache-2.0
Version
7.0.0
Deps
2
Size
113 kB
Vulns
0
Weekly
0
Stars
2.0K
Reactive Data Client

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, Vue

Schema driven. Zero updater functions.

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

Read The Docs  |  Getting Started  |  Agent Skills
Demos:   Todo  |  Github Social  |  NextJS SSR  |  Websockets+SSR

Installation

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

For more details, see the Getting Started docs page.

Skills

npx skills add reactive/data-client

Then run skill "data-client-setup"

Usage

Simple TypeScript definition
class User extends Entity {
  id = '';
  username = '';
}

class Article extends Entity {
  id = '';
  title = '';
  body = '';
  author = User.fromJS();
  createdAt = Temporal.Instant.fromEpochMilliseconds(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 Query(
  new Collection([BlogPost]),
  posts => posts.reduce((total, post) => total + post.votes, 0),
);

const totalVotes = useQuery(queryTotalVotes);
const totalVotesForUser = useQuery(queryTotalVotes, { userId });
const groupTodoByUser = new 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

Examples

  • Todo: GitHub | Sandbox | Edit on CodeSandbox
  • Github: GitHub | Sandbox
  • NextJS: GitHub | Sandbox | Edit on CodeSandbox
  • Websockets: GitHub | Sandbox | Website

API

Reactive Applications
Define Data
Networking definition
Data model
Data Type Mutable Schema Description Queryable
Object Entity, EntityMixin single unique object
Union(Entity) polymorphic objects (A | B)
Object statically known keys
Invalidate(Entity) delete an entity
List Collection(Array) growable lists
Array immutable lists
All list of all entities of a kind
Map Collection(Values) growable maps
Values immutable maps
Scalar Scalar lens-dependent entity fields
any Query(Queryable) memoized custom transforms
Lazy(Schema) deferred denormalization

Keywords