{"id":15647386,"url":"https://github.com/lukesmurray/react-query-autosync","last_synced_at":"2025-08-02T00:37:30.364Z","repository":{"id":40702221,"uuid":"390025183","full_name":"lukesmurray/react-query-autosync","owner":"lukesmurray","description":"A react hook which lets you automatically synchronize a value to a server with react-query","archived":false,"fork":false,"pushed_at":"2023-03-06T06:55:49.000Z","size":861,"stargazers_count":48,"open_issues_count":10,"forks_count":5,"subscribers_count":2,"default_branch":"main","last_synced_at":"2025-04-14T16:18:33.182Z","etag":null,"topics":["autosave","react-query"],"latest_commit_sha":null,"homepage":"https://react-query-auto-sync.lsmurray.com/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lukesmurray.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"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,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2021-07-27T15:00:03.000Z","updated_at":"2025-03-18T21:10:43.000Z","dependencies_parsed_at":"2024-10-03T12:19:18.314Z","dependency_job_id":"9c3b2dcd-0686-43f6-9294-01de5edbd852","html_url":"https://github.com/lukesmurray/react-query-autosync","commit_stats":{"total_commits":39,"total_committers":3,"mean_commits":13.0,"dds":0.1282051282051282,"last_synced_commit":"27bcf34985468dabdeaa48b8e455f7e69c133ad7"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukesmurray%2Freact-query-autosync","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukesmurray%2Freact-query-autosync/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukesmurray%2Freact-query-autosync/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lukesmurray%2Freact-query-autosync/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lukesmurray","download_url":"https://codeload.github.com/lukesmurray/react-query-autosync/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":248914118,"owners_count":21182359,"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":["autosave","react-query"],"created_at":"2024-10-03T12:19:07.068Z","updated_at":"2025-04-14T16:18:52.523Z","avatar_url":"https://github.com/lukesmurray.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# useReactQueryAutoSync\n\n![](https://img.shields.io/npm/v/use-react-query-auto-sync?label=npm)\n![](https://img.shields.io/bundlephobia/minzip/use-react-query-auto-sync)\n\nA helpful react hook for building interfaces which require autosave.\nRead more about the motivation and design in the [original blog post](https://lsmurray.com/blog/react-query-auto-sync-hook).\nCheck out the quick example below or feel free to view the [drawing demo](https://react-query-auto-sync.lsmurray.com/) online.\nThe code for the demo is in the [src folder](./src) and can be run locally with `yarn dev`. The hook is used in the `useStrokes` function in [src/components/Demo.tsx](./src/components/Demo.tsx).\n\n## Installation\n\n```sh\n# npm\nnpm install use-react-query-auto-sync\n\n# yarn\nyarn add use-react-query-auto-sync\n```\n\n## Documentation\n\nThe library exposes two hooks `useReactQueryAutoSync` and `useReactQueryAutoSave`.\nBoth hooks return an object which contains `draft` and `setDraft` properties which can be treated similarly to the `state` and `setState` values returned by [`useState`](https://reactjs.org/docs/hooks-state.html).\nThe key thing this library does is provide mechanisms to automatically save and load changes to the `draft` value between the server and the client, all through a simple API.\nBoth hooks directly expose [react query](https://react-query.tanstack.com/) options so they are simple to configure and use.\nThis is easiest to see with an example.\n\n\u003c!-- prettier-ignore-start --\u003e\n```tsx\nfunction Example() {\n  const { draft, setDraft, queryResult } = useReactQueryAutoSync({\n    queryOptions: { /* omitted but same as react-query */ },\n    mutationOptions: { /* omitted but same as react-query */ },\n    autoSaveOptions: { wait: 1000 },\n  });\n\n  const loading = queryResult.isLoading;\n\n  if (loading) {\n    return \u003cdiv\u003eLoading...\u003c/div\u003e;\n  } else {\n    return (\n      \u003cdiv\u003e\n        \u003cinput type=\"text\" value={draft} onChange={(e) =\u003e setDraft(e.target.value)}\u003e\u003c/input\u003e\n      \u003c/div\u003e\n    );\n  }\n}\n```\n\u003c!-- prettier-ignore-end --\u003e\n\nIn this example we use query and mutation options to tell `useReactQueryAutoSync` how to fetch and save the value to the server. We use the `autoSaveOptions` parameter to tell `useReactQueryAutoSync` to debounce changes and automatically synchronize the value to the server after one second without any changes.\n\nSimilarly to `useState` you can only change the `draft` value using the `setDraft` function.\n\nIn addition to the sync hook the library exposes `useReactQueryAutoSave` (save). The difference between the two is the save hook is unidirectional and only saves a local value to the server when the local value changes. This can be useful for automatically saving things like logs, user analytics, or error reports. The sync hook is useful for things like documents where you don't want the user to have to press a save button to keep their changes.\n\n### `useReactQueryAutoSync` Parameters\n\n- `queryOptions` **required**: these are the query options passed to `useQuery`. Make sure to set `refetchInterval` if you want to enable automatic polling. React query auto sync does not support query data selectors so make sure not to pass `select`. This is because react query auto sync expects the input to the mutate function to have the same type as the return value of the query function.\n- `mutationOptions` **required**: these are the mutation options passed to `useMutation`. Internally the hook uses `onMutate`, `onError`, and `onSettled` to optimistically update the state but it will call your versions of these functions as well. The hook uses the key `previousData` to save the previous data in the `onMutate` context.\n- `autoSaveOptions`: see autoSaveOptionsBelow. If undefined the hook will not automatically save data since it will assume a debounce time of `Infinity`.\n- `merge`: function used to merge updates from the server with local changes to server data. If undefined the hook will ignore background updates from the server even if `refetchInterval` is supplied and local changes will take precedence. The merge function is also used when an error occurs while saving data.\n- `alertIfUnsavedChanges`: ask the user to confirm before leaving the page if there are unsaved changes. If undefined the hook will not ask the user to confirm before leaving.\n- `mutateEnabled`: similar to the `enabled` parameter of `useQuery`. If `mutateEnabled` is false and the hook tries to save to the server, a pending save will be created, and when `mutateEnabled` is toggled to true the pending save will immediately execute. Can be useful if you need to use dependent queries to get data to perform the mutation. If undefined, `mutateEnabled` defaults to true.\n- `draftProvider`: **experimental** see draftProviderBelow. If undefined the hook will use `useState` to create the `draft` value.\n\n### `useReactQueryAutoSave` Parameters\n\nSame as `useReactQueryAutoSync` but does not have `queryOptions`.\n\n### `autoSaveOptions`\n\n- `wait`: number of milliseconds to delay the debounce function\n- `maxWait`: maximum number of milliseconds to delay the debounce function. If undefined there is no max delay.\n\n### `draftProvider` (**experimental**)\n\n- `draft`: The current value of the draft.\n- `setDraft`: Function used to update the draft. `(value) =\u003e void`.\n\nBy default `useReactQueryAutoSync` uses `useState` to implement the draft.\nHowever there are times when this is not desired.\nFor example, if you want to display the same synchronized value in multiple places in your application you have to either [lift state up](https://reactjs.org/docs/lifting-state-up.html) or use a [react context](https://reactjs.org/docs/context.html).\nIf you try using `useReactQueryAutoSync` in multiple locations the values may eventually sync but it would be a sub optimal experience since synchronizing the values would require multiple round trips to the server.\nInstead you can use the `draftProvider` and provide your own draft values backed by a library such as recoil or jotai or zustand.\nHere is a simple example which creates a `draftProvider` using jotai.\nRegardless of where you use this hook the `draft` values will be immediately synchronized.\n\n\u003c!-- prettier-ignore-start --\u003e\n```tsx\nconst exampleAtom = atom(undefined);\n\nfunction Example() {\n  const [draft_, setDraft_] = useAtom(exampleAtom);\n  const { draft, setDraft, queryResult } = useReactQueryAutoSync({\n    queryOptions: { /* omitted */ },\n    mutationOptions: { /* omitted */ },\n    autoSaveOptions: { wait: 1000 },\n    draftProvider: { draft: draft_, setDraft: setDraft_ },\n  });\n```\n\u003c!-- prettier-ignore-end--\u003e\n\n⚠️ This is an experimental feature and has issues such as potentially issuing a mutation for each hook.\n\n\u003e For instructions on how to use `draftProvider` safely [check this issue comment](https://github.com/lukesmurray/react-query-autosync/issues/3#issuecomment-990052496).\n\n## Example\n\nHere is a more complex example which shows off more of the features of `useReactQueryAutoSync`.\n\n```ts\nimport React from \"react\";\nimport { useReactQueryAutoSync } from \"../lib/useReactQueryAutoSync\";\n\n// fake api object. You would supply your own!\nconst fakeAPI: any = {};\n\n// fake function used to merge server and local state\nconst mergeFoo: any = (remote: any, local: any) =\u003e ({ ...remote, ...local });\n\nexport function Demo() {\n  const { draft, setDraft } = useReactQueryAutoSync({\n    queryOptions: {\n      queryKey: \"foo\",\n      queryFn: async () =\u003e fakeAPI.fetchFoo(),\n      // if you want to poll the server pass a refetchInterval to react query\n      refetchInterval: 5000,\n    },\n    mutationOptions: {\n      mutationFn: async (foo) =\u003e fakeAPI.saveFoo(foo),\n    },\n    // pass autoSaveOptions to automatically save to the server with debouncing\n    autoSaveOptions: {\n      wait: 500,\n    },\n    // pass alertIfUnsavedChanges to notify user if they leave with unsaved changes\n    alertIfUnsavedChanges: true,\n    // pass merge to merge server and local state when the server state updates\n    merge: (remoteFoo, localFoo) =\u003e mergeFoo(remoteFoo, localFoo),\n  });\n\n  return (\n    \u003c\u003e\n      \u003cinput\n        type=\"text\"\n        value={draft.foo}\n        onChange={(e) =\u003e {\n          // modify draft with `setDraft` but make sure to modify a copy so you\n          // don't break the ReactQuery caching!\n          setDraft({ ...draft, foo: e.target.value });\n        }}\n      /\u003e\n    \u003c/\u003e\n  );\n}\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukesmurray%2Freact-query-autosync","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flukesmurray%2Freact-query-autosync","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flukesmurray%2Freact-query-autosync/lists"}