{"id":19083302,"url":"https://github.com/robusgauli/js-immutable","last_synced_at":"2025-04-30T08:49:23.882Z","repository":{"id":32422610,"uuid":"132852765","full_name":"RobusGauli/js-immutable","owner":"RobusGauli","description":"💎 Modern IMMUTABILITY for JavaScript. Clean and Elegant 🎗","archived":false,"fork":false,"pushed_at":"2022-12-06T17:05:05.000Z","size":1533,"stargazers_count":28,"open_issues_count":15,"forks_count":5,"subscribers_count":2,"default_branch":"master","last_synced_at":"2025-04-14T15:49:07.626Z","etag":null,"topics":["immutable","immutablejs","react-state","reducer","redux","redux-state","state-management"],"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/RobusGauli.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE.txt","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2018-05-10T05:28:42.000Z","updated_at":"2023-07-20T06:42:34.000Z","dependencies_parsed_at":"2023-01-14T21:09:51.237Z","dependency_job_id":null,"html_url":"https://github.com/RobusGauli/js-immutable","commit_stats":null,"previous_names":[],"tags_count":2,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobusGauli%2Fjs-immutable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobusGauli%2Fjs-immutable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobusGauli%2Fjs-immutable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/RobusGauli%2Fjs-immutable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/RobusGauli","download_url":"https://codeload.github.com/RobusGauli/js-immutable/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":251672577,"owners_count":21625515,"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":["immutable","immutablejs","react-state","reducer","redux","redux-state","state-management"],"created_at":"2024-11-09T02:46:52.121Z","updated_at":"2025-04-30T08:49:23.835Z","avatar_url":"https://github.com/RobusGauli.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"\u003cp align=\"center\"\u003e\n  \u003cimg src=\"https://github.com/RobusGauli/js-immutable/blob/master/assets/logo.jpg\" /\u003e\n\u003c/p\u003e\n\n\n\u003ca href=\"https://travis-ci.org/RobusGauli/js-immutable\"\u003e\n    \u003cimg src=\"https://travis-ci.org/RobusGauli/js-immutable.svg?branch=master\" hspace=\"10px\" align=\"right\" vspace=\"2px\"\u003e\n\u003c/a\u003e\n\n## Motivation 🐬🐬\nConsider the scenario where you have to set a new value to \u003cb\u003e\"temporary\"\u003c/b\u003e field without mutating original state.\n```javascript\nconst state = {\n  detail: {\n    age: 30,\n    friends: ['Roshan'],\n    personal: {\n      address: {\n        permanent: 'Kathmandu',\n        temporary: 'Pokhara'\n      },\n      spouse: 'Nancy'\n    }\n  }\n}          \n```\nJavascript way of setting a new value without modifying original state would be something like this: \n\n```javascript\n// For my brain, this is too much to wrap around just to change a single field.\n// There must be some better way. \nconst newState = {\n  ...state,\n  detail: {\n    ...state.detail,\n    personal: {\n      ...state.detail.personal,\n      address: {\n        ...state.detail.personal.address,\n        temporary: 'New Random Location' // here is the actual change\n      }\n    }\n  }\n} \n\nLook at all the repetition! This is not only annoying, but also provides a large surface area for bugs.\n```\n\n### Problems with the above code:\n:pushpin: \u003cspan\u003eNeed to \u003cb\u003ekeep track\u003c/b\u003e of whole state tree just to perform such small modification.\u003c/span\u003e\n \n :pushpin: \u003cspan\u003eNeed to make sure that state tree is not \u003cb\u003emutated\u003c/b\u003e while returning new state.\u003c/span\u003e\n \n :pushpin: Need to make sure \u003cb\u003estructure\u003c/b\u003e of state tree is not changed while returning new state. Specially it becomes nightmare in real world application where you don't know which action modified the entire redux state.\n\n:pushpin: If structure of original state tree is modified, then every action reducer must be re-written. i.e Your reducer has an \u003cb\u003edependency\u003c/b\u003e on structure of redux state.\n\n### JS Immutable in Action \n```unix\n// Add as a dependency\nnpm install js-immutable --save\n```\n```javascript\nimport reduce from 'js-immutable';\n```\n```javascript\n// create a address reducer by passing a selector\n\nconst addressReducer = reduce({\n  detail: {\n    personal: {\n      address: {\n        temporary: '#',\n      }\n    }\n  }\n})\n```\n\n```javascript\n// No mutation fear\n// State Structure is maintained\n// No dependency to the state structure while returning new state\n\nconst newState = addressReducer(state)\n  .set('New Random Location')\n  .apply();\n```\nA more complex scenario where we need to append new friend to the friends list and set new value to permanent address.\n\n```javascript\n\nconst complexReducer = reduce({\n  detail: {\n    friends: '#friends', // selector\n    personal: {\n      address: {\n        permanent: '#permanent' // selector \n      }\n    }\n  }\n});\n\n// Clean and elegant \nconst newState = complexReducer(state)\n  .of('#friends') // using friends selector and appending\n  .append('John')\n  .of('#permanent') // using permanent selector and setting\n  .set('New Value')\n  .apply();\n\n// or you can simply pipe it through predicate\n\nconst newState = complexReducer(state)\n\t.of('#friends')\n\t.pipe(friends =\u003e friends.concat('John'))\n\t.of('#permanent')\n\t.pipe(value =\u003e value.toUpperCase())\n\t.apply();\n ```\n### Note\n ###### '#' is the default selector. You don't need to use \"of(\"some selector\")' when you use '#' as a selector.\n                \n \n ### Benefits\n :pushpin: Structural Sharing out of Box. Performant!\n :pushpin: Your code is independent of the state tree and it's structure.\n \n :pushpin: You don't have to worry about mutation. Js-Immutable handles it for you.\n \n :pushpin: You only have to make changes to selector if structure of redux state tree is modified. Your reducer will never be touched in the case of state tree modification.\n\n:pushpin: It looks functional, clean, simple and easy to follow. It just makes life of your co-worker easier.\n\n \n## More on JS-Immutable\n\n#### Selector\n\nSelector are plain object that helps to select the fields on the state tree. Default selector value is '#'. The selector value must start with '#'. If your selector has multiple fields to select, Make sure they start with '#' and are unique.\n\n```javascript\n// Example of Selector\n\nconst selector = {\n  person: {\n    friends: '#' // default\n  }\n}\n// Example of using the above selector\n\nconst friendsReducer = reduce(selector);\n\nconst newState = friendsReducer(state)\n  .append('My new friend') // no need of \"of('#')\" since it is the default one.\n  .apply();\n```\n```javascript\n// Example of multiple selector\n\nconst nextSelector = {\n  name: '#name', // named selector (unique)\n  detail: {\n    address: '#address' // named selector (unique)\n  }\n}\n\n// Example of using the above selector\n\nconst multipleReducer = reduce(nextSelector);\n\nconst newState = multipleReducer(state)\n\t.of('#name')\n\t.set('New Name')\n\t.of('#address')\n\t.merge({temporary: 'Pokhara'})\n\t.apply();\n```\n\n#### Available Methods\n\u003ch4\u003e 💧  set(value: any)\u003c/h4\u003eSets the new value to the target field.\n\u003ch4\u003e 💧  append(value: any)\u003c/h4\u003eAppends new value on the target array.\n\u003ch4\u003e 💧  merge({key: value})\u003c/h4\u003e Merge object on the target object.\n\u003ch4\u003e 💧  extend([]: any)\u003c/h4\u003e Concatenate array on the target array\n\u003ch4\u003e 💧  delete(key: any)\u003c/h4\u003e Delete a key on the target object or target array.\n\u003ch4\u003e 💧  pipe(predicate: function)\u003c/h4\u003e Applies a function/predicate to the target value.\n\n### Utility Method\n\u003ch4\u003e 💧 Of(selectorName: any)\u003c/h4\u003e\n\nHelps to select the specific target so that it apply transformation to that target in the object.\n\n```javascript\n// if we have a multiple targets in a single selector\nconst selector = {\n  task: {\n    done: '#done',\n    taskDetail: '#taskDetail'\n  }\n}\nconst taskReducer = reduce(selector)\n\nconst newState = taskReducer\n\t.of('#done')\n\t.set(true)\n\t.of('#taskDetail')\n\t.set('some new Detail')\n\t.apply();\n```\n\n#### Note:\nIf you think I have missed methods that is crucially important, then please send a Pull Request.\n\n### License\n\nCopyright © 2015-2016 Robus, LLC. This source code is licensed under the MIT license found in\nthe [LICENSE.txt]\nThe documentation to the project is licensed under the [CC BY-SA 4.0](http://creativecommons.org/licenses/by-sa/4.0/)\nlicense.\n\n\n---\nMade with ♥ by Robus Gauli ([@robusgauli]\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobusgauli%2Fjs-immutable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Frobusgauli%2Fjs-immutable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Frobusgauli%2Fjs-immutable/lists"}