All Projects → alexeyraspopov → react-warehouse

alexeyraspopov / react-warehouse

Licence: MIT license
React resource loader implementation

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to react-warehouse

Use Http
🐶 React hook for making isomorphic http requests
Stars: ✭ 2,066 (+5802.86%)
Mutual labels:  suspense, react-hooks
The Platform
Web. Components. 😂
Stars: ✭ 4,355 (+12342.86%)
Mutual labels:  suspense, react-hooks
react-europe-2019
Slides and demo app from my keynote
Stars: ✭ 29 (-17.14%)
Mutual labels:  suspense, react-hooks
use-async-resource
A custom React hook for simple data fetching with React Suspense
Stars: ✭ 92 (+162.86%)
Mutual labels:  suspense, react-hooks
processor
A simple and lightweight JavaScript data processing tool. Live demo:
Stars: ✭ 27 (-22.86%)
Mutual labels:  react-hooks
react-use-hubspot-form
Embed HubSpot forms into your React components using hooks! Works with Create React App, Gatsby and other platforms.
Stars: ✭ 41 (+17.14%)
Mutual labels:  react-hooks
web
React hooks done right, for browser and SSR.
Stars: ✭ 940 (+2585.71%)
Mutual labels:  react-hooks
react-kanban-board
kanban board app with react hooks and ant design ui
Stars: ✭ 15 (-57.14%)
Mutual labels:  react-hooks
react-live2d
在react项目里展示live2d看板娘:react-live2d
Stars: ✭ 52 (+48.57%)
Mutual labels:  react-hooks
frontend-toolbox
Frontend tools which we used in snappmarket v2
Stars: ✭ 37 (+5.71%)
Mutual labels:  react-hooks
react-sage
Handy-dandy React hooks.
Stars: ✭ 21 (-40%)
Mutual labels:  react-hooks
next-qrcode
React hooks for generating QRCode for your next React apps.
Stars: ✭ 87 (+148.57%)
Mutual labels:  react-hooks
use-pan-and-zoom
👆+🔎 React hook for panning and zooming a container
Stars: ✭ 57 (+62.86%)
Mutual labels:  react-hooks
react-stateful
A simple implementation of React-State-Hook and React-Effect-Hook to show how React-Hooks work
Stars: ✭ 34 (-2.86%)
Mutual labels:  react-hooks
re-use
⚛️ 🎣 A collection of hooks for ReasonReact
Stars: ✭ 27 (-22.86%)
Mutual labels:  react-hooks
Forum-App-React-Frontend
This is the React client that consumes the API built here: https://github.com/victorsteven/Forum-App-Go-Backend
Stars: ✭ 39 (+11.43%)
Mutual labels:  react-hooks
Blog-React-Hook-Tutorial
This is a beginner tutorial for react hooks I have written in dev.to and Medium
Stars: ✭ 21 (-40%)
Mutual labels:  react-hooks
crud-app
❄️ A simple and beautiful CRUD application built with React.
Stars: ✭ 61 (+74.29%)
Mutual labels:  react-hooks
MonsterJS
MonsterJS - a game for learning JavaScript DOM Selectors while playing!
Stars: ✭ 26 (-25.71%)
Mutual labels:  react-hooks
React-Netflix-Clone
A Fully Responsive clone of Netflix website built using React.JS as a Front-end & Firebase as a Back-end.
Stars: ✭ 91 (+160%)
Mutual labels:  react-hooks

React Warehouse

npm install react-warehouse

The package provides necessary React Hooks to create resource, load and preload its entities using React Suspense (in both legacy and concurrent mode). The implementation attempts to invalidate stale data and keep things up to date without additional effort from developers and without sacrificing user experience.

Abilities & Restrictions

What the API can do:

  • Basic co-located data fetching with Suspense
    The API covers the most common data fetching use cases, including pagination, search results, etc.
  • Render-as-you-fetch approach
    The API allows implementing the approach suggested by React team. See more in corresponding section of docs.
  • Request cancellation
    When a cancel handler is available, the lib will attempt to use it whenever it's possible. Currently, Suspense has some limitations, but there are cases where redundant requests can be cancelled nevertheless.

What the API cannot do at the moment:

  • Server-side rendering
    Suspense is not yet supported in SSR. After react-dom starts support the feature, some additional changes may (or may not) be required to make the solution work properly.
  • Manual cache invalidation
    I'm trying to figure the semantics. In order to keep the solution small and focused, I'm looking for a proper level of abstraction that needs to be implemented.

Things on the roadmap:

  • Controlled mutations
    There has to be a piece of API that allows a way to perform async mutation along with updating the cache.

Usage

Render as you fetch

To be defined

Local-first & Refactoring-friendly

To be defined

Opt-in waterfall requests

If a component requires data queried from different sources where one piece depends on another, you can bypass "render as you fetch" pattern and request data directly where it's going to be used.

Note: useResourceSync() only works with pre-defined resources since it can't rely on the component's state.

import { useResourceValue, useResourceSync } from 'react-warehouse';

function FriendList({ user$ }) {
  let user = useResourceValue(user$);
  let friends = useResourceSync(FriendsResource, [user.id]);
  return (
    <section>
      {friends.map(friend => ...)}
    </section>
  );
}

Controlling max age and cache capacity

There are plenty of cases where you may know specific max age for data, or even consider some data immutable. Max age handling becomes important when users keep long living tabs with your application and keep using them without reloading.

For example, it is safe to assume that employees list is not updating too often, so we can avoid unnecessary requests by keeping data for at least 12 hours.

let Employees = createResource({
  query() { ... },
  maxAge: 12 * 60 * 60 * 1000,
});

Some pieces of data may be considered immutable in real world, which means we can avoid invalidating it based on age therefore reducing page flickering.

let PokemonAbilities = createResource({
  query(id) { ... },
  maxAge: Infinity,
});

Cache capacity makes sure the cache size don't create performance and memory issues for the application. Modifying it is not necessary in most cases, but there are some that can take advantage of it.

For example, when performing real time search request, we can set capacity: 1 which means we only need the latest result of the search saved. So whenever users type slowly and producing intermediate requests, the resource can cancel them and remove from the cache.

let UserSearch = createResource({
  query(searchString) {
    let url = `/api/users?query=${searchString}`;
    let controller = new AbortController();
    let onCancel = () => controller.abort();
    let request = fetch(url, { signal: controller.signal }).then((response) => response.json());
    return [request, onCancel];
  },
  capacity: 1,
});

When user goes through pages of content, they only see a single page. However, they may need to "go back to previous page" so it would be better to keep it for at least some time.

When they click through pages quickly, we don't need to process all requests, so clicking 6 times on "next page" really fast, will produce 6 requests, but 3 of them will be cancelled based on capacity.

API Reference

The API designed in the way that does not require specific non-local changes. The hooks can be added to existing components without refactoring the whole implementation.

createResource(options)

This function can be treated as React's createContext() function. Returns Resource instance that will be consumed by following hooks.

  • options.query — function that does the job. Must return a payload, or promise of payload, or tuple [Promise, onCancel]. See usage examples.
  • options.mutate (optional) — function that describes arbitrary mutations and returns a new resource value that will be saved in cache.
  • options.maxAge (optional) — Max resource age in milliseconds. Default is 10000.
  • options.capacity (optional) — Max cache size allowed. Default is 256.

useResource(Resource, [...deps])

Returns an instance of resource while preloading data using query(...deps) and caching the result with Resources cache options.

useResourceFactory(query, [...deps])

Returns an instance of resource while preloading data using query(...deps) and keeping the instance as a part of the calling component.

useResourceFlow(Resource, [...deps])

Returns a pair of [resource, isPending] where resource is the same as from useResource() and isPending is a boolean flag which turns true for any subsequent request after the first request is resolved.

useResourceValue(resource)

Unwraps resource instance's value and suspends if necessary.

useResourceSync(Resource, [...deps])

A composition of useResource() and useResourceValue() that allows suspending in the component which makes use of the resolved data. Suitable when waterfall is needed.

useResourceMutation(Resource, resource)

Provides a callback that uses Resource.mutate() function to update resource value and cache.

<ErrorBoundary fallback={...} onError={...} />

An optional implementation of Error Boundary. When not used, will be tree-shaked out of the bundle.

Typings

The project includes typings for both Flow and TypeScript without requiring installation of additional packages. Most of the types working under the hood providing developer experience benefits. There two types that can be used for annotating resource's query function and components props.

It is recommended to provide explicit type annotation to query() functions.

type ResourceQuery<Data>

A union type of variants that query() can return. The usage is optional since specific result type can be specified instead.

import { createResource } from 'react-warehouse';
// type ResourceQuery<Data> = Promise<Data> | [Promise<Data>, () => void]
import type { ResourceQuery } from 'react-warehouse';

type User = { id: string, fullName: string };

let UserInfo = createResource({
  query(userId: string): ResourceQuery<User> {
    return ...;
  },
});

type Resource<Data>

Represents a resource instance that is passed from parent component to child that later suspends. The usage is optional since necessary hooks are typed. Explicit usage is needed when a component's annotation is required.

import { useResourceValue } from 'react-warehouse';
import type { Resource } from 'react-warehouse';

type User = { id: string, fullName: string };
type Props = { user$: Resource<User> };

export function UserInfoView({ user$ }: Props) {
  let user = useResourceValue(user$);
  return ...;
}
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].