{"id":22206676,"url":"https://github.com/alexeyraspopov/react-warehouse","last_synced_at":"2025-10-03T17:51:51.786Z","repository":{"id":57347541,"uuid":"164161735","full_name":"alexeyraspopov/react-warehouse","owner":"alexeyraspopov","description":"React resource loader implementation","archived":false,"fork":false,"pushed_at":"2021-07-12T20:55:46.000Z","size":179,"stargazers_count":34,"open_issues_count":0,"forks_count":2,"subscribers_count":1,"default_branch":"master","last_synced_at":"2024-11-28T20:47:43.347Z","etag":null,"topics":["react","react-hooks","suspense"],"latest_commit_sha":null,"homepage":"https://alexeyraspopov.github.io/react-warehouse","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/alexeyraspopov.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2019-01-04T22:41:40.000Z","updated_at":"2023-09-22T18:17:57.000Z","dependencies_parsed_at":"2022-08-28T03:01:18.353Z","dependency_job_id":null,"html_url":"https://github.com/alexeyraspopov/react-warehouse","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeyraspopov%2Freact-warehouse","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeyraspopov%2Freact-warehouse/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeyraspopov%2Freact-warehouse/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/alexeyraspopov%2Freact-warehouse/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/alexeyraspopov","download_url":"https://codeload.github.com/alexeyraspopov/react-warehouse/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":227777712,"owners_count":17818455,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["react","react-hooks","suspense"],"created_at":"2024-12-02T18:16:00.886Z","updated_at":"2025-10-03T17:51:51.707Z","avatar_url":"https://github.com/alexeyraspopov.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# React Warehouse\n\n    npm install react-warehouse\n\nThe package provides necessary [React Hooks][react-hooks] to create resource,\nload and preload its entities using [React Suspense][react-suspense] (in both\nlegacy and concurrent mode). The implementation attempts to invalidate stale\ndata and keep things up to date without additional effort from developers and\nwithout sacrificing user experience.\n\n- [Abilities \u0026 Restrictions](#abilities--restrictions)\n- [Implementation Details](#implementation)\n- [Usage Examples \u0026 Recipes](#usage)\n  - [Render as you fetch](#render-as-you-fetch)\n  - [Local-first \u0026 Refactoring-friendly](#local-first--refactoring-friendly)\n  - [Opt-in waterfall requests](#opt-in-waterfall-requests)\n  - [Controlling max age and cache capacity](#controlling-max-age-and-cache-capacity)\n- [API Reference](#api-reference)\n- [Typings](#typings)\n\n## Abilities \u0026 Restrictions\n\nWhat the API can do:\n\n- **Basic co-located data fetching with Suspense**  \n  The API covers the most common data fetching use cases, including pagination,\n  search results, etc.\n- **Render-as-you-fetch approach**  \n  The API allows implementing the approach suggested by React team. See more\n  in corresponding [section of docs][render-as-you-fetch].\n- **Request cancellation**  \n  When a cancel handler is available, the lib will attempt to use it whenever\n  it's possible. Currently, Suspense has some limitations, but there are cases\n  where redundant requests can be cancelled nevertheless.\n\nWhat the API _cannot do_ at the moment:\n\n- **Server-side rendering**  \n  Suspense is not yet supported in SSR. After `react-dom` starts support\n  the feature, some additional changes may (or may not) be required to make\n  the solution work properly.\n- **Manual cache invalidation**  \n  I'm trying to figure the semantics. In order to keep the solution small\n  and focused, I'm looking for a proper level of abstraction that needs\n  to be implemented.\n\nThings on the roadmap:\n\n- **Controlled mutations**  \n  There has to be a piece of API that allows a way to perform async mutation\n  along with updating the cache.\n\n## Usage\n\n### Render as you fetch\n\n_To be defined_\n\n### Local-first \u0026 Refactoring-friendly\n\n_To be defined_\n\n### Opt-in waterfall requests\n\nIf a component requires data queried from different sources where one piece\ndepends on another, you can bypass \"render as you fetch\" pattern and request\ndata directly where it's going to be used.\n\nNote: `useResourceSync()` only works with pre-defined resources since it can't\nrely on the component's state.\n\n```javascript\nimport { useResourceValue, useResourceSync } from 'react-warehouse';\n\nfunction FriendList({ user$ }) {\n  let user = useResourceValue(user$);\n  let friends = useResourceSync(FriendsResource, [user.id]);\n  return (\n    \u003csection\u003e\n      {friends.map(friend =\u003e ...)}\n    \u003c/section\u003e\n  );\n}\n```\n\n### Controlling max age and cache capacity\n\nThere are plenty of cases where you may know specific max age for data, or even\nconsider some data immutable. Max age handling becomes important when users\nkeep long living tabs with your application and keep using them without reloading.\n\nFor example, it is safe to assume that employees list is not updating too often,\nso we can avoid unnecessary requests by keeping data for at least 12 hours.\n\n```javascript\nlet Employees = createResource({\n  query() { ... },\n  maxAge: 12 * 60 * 60 * 1000,\n});\n```\n\nSome pieces of data may be considered immutable in real world, which means we\ncan avoid invalidating it based on age therefore reducing page flickering.\n\n```javascript\nlet PokemonAbilities = createResource({\n  query(id) { ... },\n  maxAge: Infinity,\n});\n```\n\nCache capacity makes sure the cache size don't create performance and memory\nissues for the application. Modifying it is not necessary in most cases, but\nthere are some that can take advantage of it.\n\nFor example, when performing real time search request, we can set `capacity: 1`\nwhich means we only need the latest result of the search saved. So whenever\nusers type slowly and producing intermediate requests, the resource can cancel\nthem and remove from the cache.\n\n```javascript\nlet UserSearch = createResource({\n  query(searchString) {\n    let url = `/api/users?query=${searchString}`;\n    let controller = new AbortController();\n    let onCancel = () =\u003e controller.abort();\n    let request = fetch(url, { signal: controller.signal }).then((response) =\u003e response.json());\n    return [request, onCancel];\n  },\n  capacity: 1,\n});\n```\n\nWhen user goes through pages of content, they only see a single page. However,\nthey may need to \"go back to previous page\" so it would be better to keep it\nfor at least some time.\n\nWhen they click through pages quickly, we don't need to process all requests,\nso clicking 6 times on \"next page\" really fast, will produce 6 requests, but 3\nof them will be cancelled based on capacity.\n\n## API Reference\n\nThe API designed in the way that does not require specific non-local changes.\nThe hooks can be added to existing components without refactoring the whole\nimplementation.\n\n### `createResource(options)`\n\nThis function can be treated as React's `createContext()` function.\nReturns `Resource` instance that will be consumed by following hooks.\n\n- `options.query` — function that does the job. Must return a payload, or\n  promise of payload, or tuple `[Promise, onCancel]`. See usage examples.\n- `options.mutate` _(optional)_ — function that describes arbitrary mutations\n  and returns a new resource value that will be saved in cache.\n- `options.maxAge` _(optional)_ — Max resource age in milliseconds. Default is `10000`.\n- `options.capacity` _(optional)_ — Max cache size allowed. Default is `256`.\n\n### `useResource(Resource, [...deps])`\n\nReturns an instance of resource while preloading data using `query(...deps)`\nand caching the result with `Resource`s cache options.\n\n### `useResourceFactory(query, [...deps])`\n\nReturns an instance of resource while preloading data using `query(...deps)`\nand keeping the instance as a part of the calling component.\n\n### `useResourceFlow(Resource, [...deps])`\n\nReturns a pair of `[resource, isPending]` where `resource` is the same as from\n`useResource()` and `isPending` is a boolean flag which turns `true` for any\nsubsequent request after the first request is resolved.\n\n### `useResourceValue(resource)`\n\nUnwraps resource instance's value and suspends if necessary.\n\n### `useResourceSync(Resource, [...deps])`\n\nA composition of `useResource()` and `useResourceValue()` that allows suspending\nin the component which makes use of the resolved data. Suitable when waterfall\nis needed.\n\n### `useResourceMutation(Resource, resource)`\n\nProvides a callback that uses `Resource.mutate()` function to update `resource`\nvalue and cache.\n\n### `\u003cErrorBoundary fallback={...} onError={...} /\u003e`\n\nAn optional implementation of [Error Boundary][error-boundary]. When not used,\nwill be tree-shaked out of the bundle.\n\n## Typings\n\nThe project includes typings for both Flow and TypeScript without requiring\ninstallation of additional packages. Most of the types working under the hood\nproviding developer experience benefits. There two types that can be used for\nannotating resource's query function and components props.\n\n_It is recommended to provide explicit type annotation to `query()` functions._\n\n### `type ResourceQuery\u003cData\u003e`\n\nA union type of variants that `query()` can return. The usage is optional since\nspecific result type can be specified instead.\n\n```javascript\nimport { createResource } from 'react-warehouse';\n// type ResourceQuery\u003cData\u003e = Promise\u003cData\u003e | [Promise\u003cData\u003e, () =\u003e void]\nimport type { ResourceQuery } from 'react-warehouse';\n\ntype User = { id: string, fullName: string };\n\nlet UserInfo = createResource({\n  query(userId: string): ResourceQuery\u003cUser\u003e {\n    return ...;\n  },\n});\n```\n\n### `type Resource\u003cData\u003e`\n\nRepresents a resource instance that is passed from parent component to child\nthat later suspends. The usage is optional since necessary hooks are typed.\nExplicit usage is needed when a component's annotation is required.\n\n```javascript\nimport { useResourceValue } from 'react-warehouse';\nimport type { Resource } from 'react-warehouse';\n\ntype User = { id: string, fullName: string };\ntype Props = { user$: Resource\u003cUser\u003e };\n\nexport function UserInfoView({ user$ }: Props) {\n  let user = useResourceValue(user$);\n  return ...;\n}\n```\n\n[react-hooks]: https://reactjs.org/docs/hooks-intro.html\n[react-suspense]: https://reactjs.org/docs/concurrent-mode-suspense.html\n[lru-cache]: https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU)\n[ref-counting]: https://en.wikipedia.org/wiki/Reference_counting\n[concurrent-suspense]: https://reactjs.org/docs/concurrent-mode-suspense.html\n[render-as-you-fetch]: https://reactjs.org/docs/concurrent-mode-suspense.html#approach-3-render-as-you-fetch-using-suspense\n[error-boundary]: https://reactjs.org/docs/error-boundaries.html\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexeyraspopov%2Freact-warehouse","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Falexeyraspopov%2Freact-warehouse","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Falexeyraspopov%2Freact-warehouse/lists"}