{"id":16807052,"url":"https://github.com/lscheibel/redux-yjs-bindings","last_synced_at":"2025-03-17T03:31:27.937Z","repository":{"id":43335128,"uuid":"446199294","full_name":"lscheibel/redux-yjs-bindings","owner":"lscheibel","description":"Use Yjs to sync your Redux store with other peers!","archived":false,"fork":false,"pushed_at":"2023-07-27T21:57:57.000Z","size":2103,"stargazers_count":36,"open_issues_count":6,"forks_count":5,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-03-13T03:49:20.802Z","etag":null,"topics":["redux","yjs"],"latest_commit_sha":null,"homepage":"https://lscheibel.github.io/redux-yjs-bindings/","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/lscheibel.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":"2022-01-09T20:47:52.000Z","updated_at":"2024-12-28T04:32:12.000Z","dependencies_parsed_at":"2024-10-27T11:57:45.798Z","dependency_job_id":"995fa434-2a00-47d9-87fb-5a65e90ba7fa","html_url":"https://github.com/lscheibel/redux-yjs-bindings","commit_stats":{"total_commits":37,"total_committers":6,"mean_commits":6.166666666666667,"dds":"0.21621621621621623","last_synced_commit":"152bde4916f6a909321e60fd7ddfad1662272866"},"previous_names":[],"tags_count":8,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lscheibel%2Fredux-yjs-bindings","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lscheibel%2Fredux-yjs-bindings/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lscheibel%2Fredux-yjs-bindings/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lscheibel%2Fredux-yjs-bindings/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lscheibel","download_url":"https://codeload.github.com/lscheibel/redux-yjs-bindings/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":243841203,"owners_count":20356443,"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":["redux","yjs"],"created_at":"2024-10-13T09:52:45.072Z","updated_at":"2025-03-17T03:31:27.465Z","avatar_url":"https://github.com/lscheibel.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# redux-yjs-bindings\n\n[![npm package][npm-img]][npm-url]\n[![Downloads][downloads-img]][downloads-url]\n[![Issues][issues-img]][issues-url]\n\n\u003e Use [Yjs](https://yjs.dev/) to sync your [Redux](https://redux.js.org/) store with other peers!\n\nThis very small (roughly 1kB) bridge to connect Redux with a Yjs,\nallows you to use the synchronization features of Yjs with the data management capabilities of Redux.\nIt works with any Redux store, whether you use Redux Toolkit or not, and even supports initial values.\nValues that are transmitted over the network can be any JSON serializable value, from primitives to deeply nested object structures.\n\n## Install\n\n```bash\nnpm i redux-yjs-bindings\n```\n\n## Prerequisites\n\nTo use this library you will need a [Yjs document](https://docs.yjs.dev) as well as a [Redux store](https://redux.js.org/introduction/getting-started).\nDetailed steps on how to set both up can be taken form their respective documentation.\nYou will need to configure the reducer of the state slice that you want to have synced to accept an action dispatched by this library,\nhowever helper functions are exported to hide this away. Check below for example usage:\n\n## General Usage\n\n```ts\nimport { bind, enhanceReducer } from 'redux-yjs-bindings';\nimport { Doc } from 'yjs';\nimport { createStore } from 'redux';\nimport { mySharedStateReducer } from './store/reducers';\n\n// Create Yjs document, or use existing one.\nconst yDoc = new Doc();\n\nconst rootReducer = {\n  // Set up reducer to handle action dispatched by `redux-yjs-bindings` when remote changes come in.\n  mySharedState: enhanceReducer(mySharedStateReducer),\n};\nconst store = createStore(rootReducer);\n\n// Start synchronisation of Redux and Yjs\nbind(yDoc, store, 'mySharedState');\n```\n\nInternally the `bind` function creates a new `Y.Map()` on the provided document.\nThis map then contains a property with the provided sliceName, whose value should then always match the one of your store slice.\n\n## Limitations\n\nDespite best efforts to make this library as compatible with existing projects as possible, there are some things to keep in mind.\n\n### Referential Equality\n\nFor now referential equality is not kept when changes from a remote peer are applied to the local Redux state.\nThis may change in the future, however for now you can simply provide a [custom equality function to the useSelector hook](https://react-redux.js.org/api/hooks#equality-comparisons-and-updates)\nif performance becomes a problem.\n\n### Undefined in Array (`[undefined]`) Not Supported\n\nAs per [Redux guidelines](https://redux.js.org/style-guide/style-guide#do-not-put-non-serializable-values-in-state-or-actions)\nvalues in the store should be JSON serializable. While undefined values are mostly supported by this library,\nYjs currently doesn't support undefined in arrays. However, undefined as a primitive or as object values works\njust fine, even though they are technically not a part of JSON.\n\n### Setter Action Must Be Synchronous\n\nWhen remote changes come in through Yjs changes must be applied to the store immediately.\nSince reducers are already required to be synchronous by Redux and the action is dispatched by the library itself,\nthis should not be a problem in practice. However, in theory it would be possible for a Redux middleware to delay all\ndispatched actions, which could potentially lead to data loss.\n\n### Intention Preservation on Array Changes\n\nInternally redux-yjs-bindings subscribes to changes on the Redux store in order to patch the Yjs document.\nBy calculating the difference between the previous and the next state, intentions behind changes can be\nretrieved even though Redux forces immutability. Unfortunately diffing arrays is not trivial and the currently used\nalgorithm \"recursive-diff\" by [@cosmicanant](https://github.com/cosmicanant) does not preserve intention for insertions\nor deletions that are not at the end of the array. States will still be synced correctly,\nbut sometimes with more work than necessary.\nSee [this issue](https://github.com/lscheibel/redux-yjs-bindings/issues/3) for more details.\n\n### Holey Arrays (`[1, , 'a']`) Not Supported\n\nWell this obscure \"feature\" of JavaScript is simply not supported and will fail almost immediately.\n\n## API\n\n---\n\n### bind(yDoc: Y.Doc, store: Store, sliceName: string): unbind()\n\n#### yDoc\n\nThe yjs document changes should be synced with.\n\n#### store\n\nThe redux store changes should be synced with.\n\n#### sliceName\n\nThe name of the state slice (subtree) that should be synced.\n\n#### unbind()\n\nReturns a function which unbinds the state sync between the YJS doc and redux store\n\n---\n\n### enhanceReducer(currentReducer: Reducer): Reducer\n\n#### currentReducer\n\nThe reducer for the slice of the store that is supposed to be synced.\n\n---\n\n### Constants\n\n`ROOT_MAP_NAME` Property name of the map on the provided yDoc. Can be used to persist the yjs values on a server.\n\n`SET_STATE_FROM_YJS_ACTION` Used by `enhanceYjsReducer`. Action type that is dispatched whenever changes to yjs from other peers come in.\n\n---\n\n## Development\n\n### Setup\n\n- clone repo `git clone https://github.com/lscheibel/redux-yjs-bindings`\n- install dependencies and start [microbundle](https://github.com/developit/microbundle) in watch mode `npm i \u0026\u0026 npm dev`\n- open new terminal and navigate to example you want to test on (e.g., `cd examples/todo-mvc`)\n- follow instructions on how to start example from `README.md` but usually includes `npm i \u0026\u0026 npm start` plus starting the yjs-webrtc signaling server.\n\n### Inner Workings\n\nInternally this library simply subscribes to changes on both the Redux store and the Yjs document,\nwhile making sure not to react to notifications triggered by itself. The `subscribe` method on the\nRedux store can be used to keep track of a previous and current state, from which a difference is calculated\n(currently using \"recursive-diff\" by [@cosmicanant](https://github.com/cosmicanant)).\nThe returned list of patch operations is then iterated over to apply all changes to the Yjs document.\nIn the other direction, incoming changes from remote peers through Yjs are handled synchronously in order\nto prevent local Redux state updates while currently handling remote changes.\nWhen remote changes are detected, the Yjs state instance is simply turned into a JavaScript object,\nwhich replaces the entire store slice. This comes with the upside, that Yjs is always treated as the source of truth,\nhowever referential equality is lost, potentially impacting performance by effectively circumventing\nRedux selector's caching system, that would usually prevent unnecessary rerenders.\n\n## Acknowledgements and Prior Art\n\nSpecial thanks to Joseph Miles whose [zustand-middleware-yjs](https://github.com/joebobmiles/zustand-middleware-yjs) library was an inspiration.\nAlso look out for [Sana Labs](https://github.com/sanalabs) upcoming \"collaboration-kit\".\n\n[downloads-img]: https://img.shields.io/npm/dt/redux-yjs-bindings\n[downloads-url]: https://www.npmtrends.com/redux-yjs-bindings\n[npm-img]: https://img.shields.io/npm/v/redux-yjs-bindings\n[npm-url]: https://www.npmjs.com/package/redux-yjs-bindings\n[issues-img]: https://img.shields.io/github/issues/lscheibel/redux-yjs-bindings\n[issues-url]: https://github.com/lscheibel/redux-yjs-bindings/issues\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flscheibel%2Fredux-yjs-bindings","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flscheibel%2Fredux-yjs-bindings","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flscheibel%2Fredux-yjs-bindings/lists"}