{"id":13452122,"url":"https://github.com/aweary/react-copy-write","last_synced_at":"2025-05-16T04:00:21.904Z","repository":{"id":66095981,"uuid":"128696670","full_name":"aweary/react-copy-write","owner":"aweary","description":"✍️ Immutable state with a mutable API ","archived":false,"fork":false,"pushed_at":"2019-02-12T15:41:33.000Z","size":146,"stargazers_count":1781,"open_issues_count":17,"forks_count":54,"subscribers_count":23,"default_branch":"master","last_synced_at":"2025-04-08T14:05:38.036Z","etag":null,"topics":["copy-on-write","immer","immutable","react","state"],"latest_commit_sha":null,"homepage":"","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/aweary.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}},"created_at":"2018-04-09T01:14:52.000Z","updated_at":"2025-03-31T10:24:26.000Z","dependencies_parsed_at":null,"dependency_job_id":"3a33351c-df2f-4384-a6ee-072d39887d06","html_url":"https://github.com/aweary/react-copy-write","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aweary%2Freact-copy-write","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aweary%2Freact-copy-write/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aweary%2Freact-copy-write/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aweary%2Freact-copy-write/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aweary","download_url":"https://codeload.github.com/aweary/react-copy-write/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254464890,"owners_count":22075570,"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":["copy-on-write","immer","immutable","react","state"],"created_at":"2024-07-31T07:01:13.775Z","updated_at":"2025-05-16T04:00:21.871Z","avatar_url":"https://github.com/aweary.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","react","List"],"sub_categories":[],"readme":"\u003cdiv align=\"center\"\u003e\n\u003ch1\u003ereact-copy-write\u003c/h1\u003e\n\n\u003ca href=\"https://emojipedia.org/writing-hand/\"\u003e\n\u003cimg height=\"80\" width=\"80\" alt=\"goat\" src=\"https://emojipedia-us.s3.amazonaws.com/thumbs/240/twitter/131/writing-hand_270d.png\" /\u003e\n\u003c/a\u003e\n\nAn immutable React state management library with a simple mutable API, memoized selectors, and structural sharing. Powered by [Immer](https://github.com/mweststrate/immer).\n\n\u003c/div\u003e\n\n## Overview\n\nThe benefits of immutable state are clear, but maintaining that immutable state can sometimes be burdensome and verbose: updating a value more than one or two levels deep in your state tree can require lots of object/array spreading, and it's relatively easy to accidentally mutate something.\n\nreact-copy-write lets you use straightforward mutations to update an immutable state tree, thanks to [Immer](https://github.com/mweststrate/immer). Since Immer uses the [copy-on-write](https://en.wikipedia.org/wiki/Copy-on-write) technique to update immutable values, we get the benefits of structural sharing and memoization. This means react-copy-write not only lets you use simple mutations to update state, but it's also very efficient about re-rendering.\n\n## Documentation\n\nreact-copy-write is currently under-going significant API changes as it's tested in a production environment. Most documentation has been removed until we arrive at a stable API. Below you will find a bare-bones API reference that should get you started.\n\n\n# `createState`\n\nThe default export of the package. Takes in an initial state object and returns a collection of components and methods for reading, rendering, and updating state.\n\n\n```jsx\nimport createState from 'react-copy-write'\n\nconst {\n  Provider,\n  Consumer,\n  createSelector,\n  mutate,\n} = createState({name: 'Brandon' });\n```\n\n# `Provider`\n\nThe Provider component provides state to all the consumers. All Consumer instances associated with a given provider must be rendered as children of the Provider.\n\n```jsx\nconst App = () =\u003e (\n  \u003cProvider\u003e\n    \u003cAppBody /\u003e\n  \u003c/Provider\u003e\n)\n```\n\nIf you need to initialize state from props you can use the `initialState` prop to do so. Note that it only initializes state, updating `initialState` will have no effect.\n\n```jsx\nconst App = ({user}) =\u003e (\n  \u003cProvider initialState={{name: user.name }}\u003e\n    \u003cAppBody /\u003e\n  \u003c/Provider\u003e\n)\n```\n\n\n## `Consumer`\n\nA Consumer lets you _consume_ some set of state. It uses a [render prop](https://reactjs.org/docs/render-props.html#use-render-props-for-cross-cutting-concerns) as a child for accessing and rendering state. This is identical to the [React Context Consumer API](https://reactjs.org/docs/context.html#consumer).\n\n```jsx\nconst Avatar = () =\u003e (\n  \u003cConsumer\u003e\n   {state =\u003e (\n     \u003cimg src={state.user.avatar.src} /\u003e\n   )}\n  \u003c/Consumer\u003e\n)\n```\n\nThe render callback is always called with a tuple of the observed state, using an array. By default that tuple contains one element: the entire state tree.\n\n### Selecting State\n\nIf a Consumer observes the entire state tree then it will update anytime _any_ value in state changes. This is usually not what you want. You can use the `select` prop to select a set of values from state that a Consumer depends on.\n\n```jsx\nconst Avatar = () =\u003e (\n  \u003cConsumer select={[state =\u003e state.user.avatar.src]}\u003e\n    {src =\u003e \u003cimg src={src} /\u003e}\n  \u003c/Consumer\u003e\n)\n```\n\nNow the Avatar component will only re-render if `state.user.avatar.src` changes. If a component depends on multiple state values you can just pass in more selectors.\n\n```jsx\nconst Avatar = () =\u003e (\n  \u003cConsumer select={[\n    state =\u003e state.user.avatar.src,\n    state =\u003e state.theme.avatar,\n  ]}\u003e\n    {(src, avatarTheme) =\u003e \u003cimg src={src} style={avatarTheme} /\u003e}\n  \u003c/Consumer\u003e\n)\n```\n\n## Updating State\n\n`createState` also returns a `mutate` function that you can use to make state updates.\n\n```js\nconst {mutate, Consumer, Provider} = createState({...})\n```\n\nMutate takes a single function as an argument, which will be passed a \"draft\" of the current state. This draft is a mutable copy that you can edit directly with simple mutations\n\n```js\nconst addTodo = todo =\u003e {\n  mutate(draft =\u003e {\n    draft.todos.push(todo);\n  })\n}\n```\n\nYou don't have to worry about creating new objects or arrays if you're only updating a single item or property. \n\n```js\nconst updateUserName = (id, name) =\u003e {\n  mutate(draft =\u003e {\n    // No object spread required 😍\n    draft.users[id].name = name;\n    draft.users[id].lastUpdate = Date.now();\n  })\n}\n```\n\nCheck out [the Immer docs for more information](https://github.com/mweststrate/immer).\n\nSince `mutate` is returned by `createState` you can call it anywhere. If you've used Redux you can think of it like `dispatch` in that sense.\n\n\n## Optimized Selectors\n\n`createState` also returns a `createSelector` function which you can use to create an _optimized selector_. This selector should be defined outside of render, and ideally be something you use across multiple components.\n\n```jsx\nconst selectAvatar = createSelector(state =\u003e state.user.avatar.src);\n```\n\nYou can get some really, really nice speed if you use this and follow a few rules:\n\n### Don't call `createSelector` in render.\n\n\n🚫\n```jsx\nconst App = () =\u003e (\n  // Don't do this \n  \u003cConsumer select={[createSelector(state =\u003e state.user)]}\u003e\n    {...}\n  \u003c/Consumer\u003e\n)\n```\n\n👍\n```jsx\n// Define it outside of render!\nconst selectUser = createSelector(state =\u003e state.user);\nconst App = () =\u003e (\n  \u003cConsumer select={[selectUser]}\u003e\n    {...}\n  \u003c/Consumer\u003e\n)\n```\n\n### Avoid mixing optimized and un-optimized selectors\n\n🚫\n```jsx\nconst selectUser = createSelector(state =\u003e state.user);\nconst App = () =\u003e (\n  // This isn't terrible but the consumer gets de-optimized so\n  // try to avoid it\n  \u003cConsumer select={[selectUser, state =\u003e state.theme]}\u003e\n    {...}\n  \u003c/Consumer\u003e\n)\n```\n\n👍\n```jsx\nconst selectUser = createSelector(state =\u003e state.user);\nconst selectTheme = createSelector(state =\u003e state.theme);\nconst App = () =\u003e (\n  \u003cConsumer select={[selectUser, selectTheme]}\u003e\n    {...}\n  \u003c/Consumer\u003e\n)\n```\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faweary%2Freact-copy-write","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faweary%2Freact-copy-write","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faweary%2Freact-copy-write/lists"}