{"id":13727403,"url":"https://github.com/morrys/react-relay-offline","last_synced_at":"2025-04-05T14:04:15.248Z","repository":{"id":34832395,"uuid":"183761724","full_name":"morrys/react-relay-offline","owner":"morrys","description":"TypeScript library files for Relay Modern Offline","archived":false,"fork":false,"pushed_at":"2023-03-07T17:16:26.000Z","size":7293,"stargazers_count":234,"open_issues_count":32,"forks_count":16,"subscribers_count":8,"default_branch":"master","last_synced_at":"2025-03-29T13:07:35.615Z","etag":null,"topics":["indexeddb","offline","react","react-native","relay","relay-modern","typescript"],"latest_commit_sha":null,"homepage":"https://morrys.github.io/react-relay-offline/docs/react-relay-offline.html","language":"TypeScript","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/morrys.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null},"funding":{"github":["morrys"],"custom":"https://www.paypal.me/m0rrys"}},"created_at":"2019-04-27T11:00:06.000Z","updated_at":"2025-02-19T10:39:29.000Z","dependencies_parsed_at":"2024-01-07T21:07:00.643Z","dependency_job_id":"91e44980-64bb-42fc-badc-c6147cdad49b","html_url":"https://github.com/morrys/react-relay-offline","commit_stats":{"total_commits":179,"total_committers":6,"mean_commits":"29.833333333333332","dds":"0.22346368715083798","last_synced_commit":"378bb98eb354c03621aed60f95d66aa286a434a9"},"previous_names":[],"tags_count":31,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morrys%2Freact-relay-offline","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morrys%2Freact-relay-offline/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morrys%2Freact-relay-offline/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/morrys%2Freact-relay-offline/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/morrys","download_url":"https://codeload.github.com/morrys/react-relay-offline/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247345850,"owners_count":20924102,"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":["indexeddb","offline","react","react-native","relay","relay-modern","typescript"],"created_at":"2024-08-03T01:03:54.222Z","updated_at":"2025-04-05T14:04:15.218Z","avatar_url":"https://github.com/morrys.png","language":"TypeScript","funding_links":["https://github.com/sponsors/morrys","https://www.paypal.me/m0rrys"],"categories":["TypeScript"],"sub_categories":[],"readme":"---\nid: react-relay-offline\ntitle: Getting Started\n---\n\n# [React Relay Offline](https://github.com/morrys/react-relay-offline)\n\nReact Relay Offline is a extension of [Relay](https://facebook.github.io/relay/) for offline capabilities\n\n## Installation React Web\n\nInstall react-relay and react-relay-offline using yarn or npm:\n\n```\nyarn add react-relay react-relay-offline\n```\n\n## Installation React Native\n\nInstall react-relay and react-relay-offline using yarn or npm:\n\n```\nyarn add @react-native-community/netinfo react-relay react-relay-offline\n```\n\nYou then need to do some extra configurations to run netinfo package with React Native. Please check [@react-native-community/netinfo official README.md](https://github.com/react-native-netinfo/react-native-netinfo#using-react-native--060) to get the full step guide.\n\n## Main Additional Features\n\n- automatic persistence and rehydration of the store (AsyncStorage, localStorage, IndexedDB)\n\n- configuration of persistence\n\n  - custom storage\n\n  - different key prefix (multi user)\n\n  - serialization: JSON or none\n\n- fetchPolicy network-only, store-and-network, store-or-network, store-only\n\n- management and utilities for network detection\n\n- automatic use of the policy **store-only** when the application is offline\n\n- optimization in store management and addition of **TTL** to queries in the store\n\n- offline mutation management\n\n  - backup of mutation changes\n\n  - update and publication of the mutation changes in the store\n\n  - persistence of mutation information performed\n\n  - automatic execution of mutations persisted when the application returns online\n\n  - configurability of the offline mutation execution network\n\n  - onComplete callback of the mutation performed successfully\n\n  - onDiscard callback of the failed mutation\n\n## Contributing\n\n- **Give a star** to the repository and **share it**, you will **help** the **project** and the **people** who will find it useful\n\n- **Create issues**, your **questions** are a **valuable help**\n\n- **PRs are welcome**, but it is always **better to open the issue first** so as to **help** me and other people **evaluating it**\n\n- **Please sponsor me**\n\n### Sponsors\n\n\u003ca href=\"https://memorangapp.com\" target=\"_blank\"\u003e\u003cimg height=40px src=\"https://github.com/morrys/react-relay-offline/raw/master/docs/assets/memorang-logo.png\" alt=\"Memorang\"\u003e\n\n## react-relay-offline examples\n\nThe [offline-examples](https://github.com/morrys/offline-examples) repository contains example projects on how to use react-relay-offline:\n\n  * `nextjs-ssr-preload`: using the render-as-you-fetch pattern with loadQuery in SSR contexts\n  * `nextjs`: using the QueryRenderer in SSR contexts\n  * `react-native/todo-updater`: using QueryRender in an RN application\n  * `todo-updater`: using the QueryRender\n  * `suspense/cra`: using useLazyLoadQuery in a CRA\n  * `suspense/nextjs-ssr-preload`: using the render-as-you-fetch pattern with loadLazyQuery in react concurrent + SSR contexts\n  * `suspense/nextjs-ssr`: using useLazyLoadQuery in SSR contexts\n\nTo try it out!\n\n\n## Environment\n\n```ts\nimport { Network } from \"relay-runtime\";\nimport { RecordSource, Store, Environment } from \"react-relay-offline\";\n\nconst network = Network.create(fetchQuery);\nconst recordSource = new RecordSource();\nconst store = new Store(recordSource);\nconst environment = new Environment({ network, store });\n```\n\n## Environment with Offline Options\n\n```ts\nimport { Network } from \"relay-runtime\";\nimport { RecordSource, Store, Environment } from \"react-relay-offline\";\n\nconst network = Network.create(fetchQuery);\n\nconst networkOffline = Network.create(fetchQueryOffline);\nconst manualExecution = false;\n\nconst recordSource = new RecordSource();\nconst store = new Store(recordSource);\nconst environment = new Environment({ network, store });\nenvironment.setOfflineOptions({\n  manualExecution, //optional\n  network: networkOffline, //optional\n  start: async mutations =\u003e {\n    //optional\n    console.log(\"start offline\", mutations);\n    return mutations;\n  },\n  finish: async (mutations, error) =\u003e {\n    //optional\n    console.log(\"finish offline\", error, mutations);\n  },\n  onExecute: async mutation =\u003e {\n    //optional\n    console.log(\"onExecute offline\", mutation);\n    return mutation;\n  },\n  onComplete: async options =\u003e {\n    //optional\n    console.log(\"onComplete offline\", options);\n    return true;\n  },\n  onDiscard: async options =\u003e {\n    //optional\n    console.log(\"onDiscard offline\", options);\n    return true;\n  },\n  onPublish: async offlinePayload =\u003e {\n    //optional\n    console.log(\"offlinePayload\", offlinePayload);\n    return offlinePayload;\n  }\n});\n```\n\n- manualExecution: if set to true, mutations in the queue are no longer performed automatically as soon as you go back online. invoke manually: `environment.getStoreOffline().execute();`\n\n- network: it is possible to configure a different network for the execution of mutations in the queue; all the information of the mutation saved in the offline store are inserted into the \"metadata\" field of the CacheConfig so that they can be used during communication with the server.\n\n* start: function that is called once the request queue has been started.\n\n* finish: function that is called once the request queue has been processed.\n\n* onExecute: function that is called before the request is sent to the network.\n\n* onPublish: function that is called before saving the mutation in the store\n\n* onComplete: function that is called once the request has been successfully completed. Only if the function returns the value true, the request is deleted from the queue.\n\n* onDiscard: function that is called when the request returns an error. Only if the function returns the value true, the mutation is deleted from the queue\n\n## IndexedDB\n\nlocalStorage is used as the default react web persistence, while AsyncStorage is used for react-native.\n\nTo use persistence via IndexedDB:\n\n```ts\nimport { Network } from \"relay-runtime\";\nimport EnvironmentIDB from \"react-relay-offline/lib/runtime/EnvironmentIDB\";\n\nconst network = Network.create(fetchQuery);\nconst environment = EnvironmentIDB.create({ network });\n```\n\n## Environment with PersistOfflineOptions\n\n```ts\nimport { Network } from \"relay-runtime\";\nimport { RecordSource, Store, Environment } from \"react-relay-offline\";\nimport { CacheOptions } from \"@wora/cache-persist\";\n\nconst network = Network.create(fetchQuery);\n\nconst networkOffline = Network.create(fetchQueryOffline);\n\nconst persistOfflineOptions: CacheOptions = {\n  prefix: \"app-user1\"\n};\nconst recordSource = new RecordSource();\nconst store = new Store(recordSource);\nconst environment = new Environment({ network, store }, persistOfflineOptions);\n```\n\n[CacheOptions](https://morrys.github.io/wora/docs/cache-persist.html#cache-options)\n\n## Store with custom options\n\n```ts\nimport { Store } from \"react-relay-offline\";\nimport { CacheOptions } from \"@wora/cache-persist\";\nimport { StoreOptions } from \"@wora/relay-store\";\n\nconst persistOptionsStore: CacheOptions = { };\nconst persistOptionsRecords: CacheOptions = {};\nconst relayStoreOptions: StoreOptions = { queryCacheExpirationTime: 10 * 60 * 1000 }; // default\nconst recordSource = new RecordSource(persistOptionsRecords);\nconst store = new Store(recordSource, persistOptionsStore, relayStoreOptions);\nconst environment = new Environment({ network, store });\n```\n\n\n## useQuery\n\n`useQuery` does not take an environment as an argument. Instead, it reads the environment set in the context; this also implies that it does not set any React context.\nIn addition to `query` (first argument) and `variables` (second argument), `useQuery` accepts a third argument `options`. \n\n**options**\n\n`fetchPolicy`: determine whether it should use data cached in the Relay store and whether to send a network request. The options are:\n  * `store-or-network` (default): Reuse data cached in the store; if the whole query is cached, skip the network request\n  * `store-and-network`: Reuse data cached in the store; always send a network request.\n  * `network-only`: Don't reuse data cached in the store; always send a network request. (This is the default behavior of Relay's existing `QueryRenderer`.)\n  * `store-only`: Reuse data cached in the store; never send a network request.\n\n`fetchKey`: [Optional] A fetchKey can be passed to force a refetch of the current query and variables when the component re-renders, even if the variables didn't change, or even if the component isn't remounted (similarly to how passing a different key to a React component will cause it to remount). If the fetchKey is different from the one used in the previous render, the current query and variables will be refetched.\n\n`networkCacheConfig`: [Optional] Object containing cache config options for the network layer. Note the the network layer may contain an additional query response cache which will reuse network responses for identical queries. If you want to bypass this cache completely, pass {force: true} as the value for this option. **Added the TTL property to configure a specific ttl for the query.**\n\n`skip`: [Optional] If skip is true, the query will be skipped entirely.\n\n`onComplete`: [Optional] Function that will be called whenever the fetch request has completed\n\n```ts\nimport { useQuery } from \"react-relay-offline\";\nconst networkCacheConfig = {\n  ttl: 1000\n}\nconst hooksProps = useQuery(query, variables, {\n  networkCacheConfig,\n  fetchPolicy,\n});\n```\n\n## useLazyLoadQuery\n\n```ts\nimport { useQuery } from \"react-relay-offline\";\nconst networkCacheConfig = {\n  ttl: 1000\n}\nconst hooksProps = useLazyLoadQuery(query, variables, {\n  networkCacheConfig,\n  fetchPolicy,\n});\n```\n\n## useRestore \u0026 loading\n\nthe **useRestore** hook allows you to manage the hydratation of persistent data in memory and to initialize the environment.\n\n**It must always be used before using environement in web applications without SSR \u0026 react legacy \u0026 react-native.**\n\n**Otherwise, for SSR and react concurrent applications the restore is natively managed by QueryRenderer \u0026 useQueryLazyLoad \u0026 useQuery.**\n\n```\nconst isRehydrated = useRestore(environment);\n   if (!isRehydrated) {\n     return \u003cLoading /\u003e;\n   }\n```\n\n## fetchQuery_DEPRECATED\n\n```ts\nimport { fetchQuery_DEPRECATED } from \"react-relay-offline\";\n```\n\n\n## Detect Network\n\n```ts\nimport { useIsConnected } from \"react-relay-offline\";\nimport { useNetInfo } from \"react-relay-offline\";\nimport { NetInfo } from \"react-relay-offline\";\n```\n\n## Supports Hooks from relay-hooks\n\nNow you can use hooks (useFragment, usePagination, useRefetch) from [relay-hooks](https://github.com/relay-tools/relay-hooks)\n\n## render-as-you-fetch \u0026 usePreloadedQuery\n\n### loadQuery\n\n* input parameters\n\nsame as useQuery + environment\n\n* output parameters\n  * \n    `next: \u003cTOperationType extends OperationType\u003e(\n        environment: Environment,\n        gqlQuery: GraphQLTaggedNode,\n        variables?: TOperationType['variables'],\n        options?: QueryOptions,\n    ) =\u003e Promise\u003cvoid\u003e`:  fetches data. A promise returns to allow the await in case of SSR\n  * `dispose: () =\u003e void`: cancel the subscription and dispose of the fetch\n  * `subscribe: (callback: (value: any) =\u003e any) =\u003e () =\u003e void`:  used by the usePreloadedQuery\n  * `getValue \u003cTOperationType\u003e(environment?: Environment,) =\u003e OfflineRenderProps\u003cTOperationType\u003e | Promise\u003cany\u003e`:  used by the usePreloadedQuery\n\n```ts\nimport {graphql, loadQuery} from 'react-relay-offline';\nimport {environment} from ''./environment';\n\nconst query = graphql`\n  query AppQuery($id: ID!) {\n    user(id: $id) {\n      name\n    }\n  }\n`;\n\nconst prefetch = loadQuery();\nprefetch.next(\n  environment,\n  query,\n  {id: '4'},\n  {fetchPolicy: 'store-or-network'},\n);\n// pass prefetch to usePreloadedQuery()\n```\n\n### loadLazyQuery\n\n**is the same as loadQuery but must be used with suspense**\n\n### render-as-you-fetch in SSR\n\nIn SSR contexts, **not using the useRestore hook** it is necessary to manually invoke the hydrate but without using the await.\n\nThis will allow the usePreloadedQuery hook to correctly retrieve the data from the store and once the hydration is done it will be react-relay-offline\n\nto notify any updated data in the store.\n\n```ts\n  if (!environment.isRehydrated() \u0026\u0026 ssr) {\n      environment.hydrate().then(() =\u003e {}).catch((error) =\u003e {});\n  }\n  prefetch.next(environment, QUERY_APP, variables, {\n         fetchPolicy: NETWORK_ONLY,\n  });\n```\n\n## Requirement\n\n- Version \u003e=11.0.2 of the relay-runtime library\n- When a new node is created by mutation the id must be generated in the browser to use it in the optimistic response\n\n## License\n\nReact Relay Offline is [MIT licensed](./LICENSE).\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorrys%2Freact-relay-offline","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmorrys%2Freact-relay-offline","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmorrys%2Freact-relay-offline/lists"}