{"id":20720209,"url":"https://github.com/zaaack/immuter","last_synced_at":"2025-04-23T14:27:00.071Z","repository":{"id":57272878,"uuid":"91928186","full_name":"zaaack/immuter","owner":"zaaack","description":"**DEPRECATED** for https://github.com/hydux/hydux-mutator","archived":false,"fork":false,"pushed_at":"2017-12-19T06:45:31.000Z","size":730,"stargazers_count":9,"open_issues_count":0,"forks_count":1,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-03-30T00:31:36.393Z","etag":null,"topics":["deep-properties","dotpath","flow","immutable","redux-state","type-safe","typescript"],"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/zaaack.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}},"created_at":"2017-05-21T02:24:43.000Z","updated_at":"2018-08-29T03:07:01.000Z","dependencies_parsed_at":"2022-09-13T12:13:08.467Z","dependency_job_id":null,"html_url":"https://github.com/zaaack/immuter","commit_stats":null,"previous_names":[],"tags_count":11,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaaack%2Fimmuter","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaaack%2Fimmuter/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaaack%2Fimmuter/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/zaaack%2Fimmuter/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/zaaack","download_url":"https://codeload.github.com/zaaack/immuter/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":250450590,"owners_count":21432678,"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":["deep-properties","dotpath","flow","immutable","redux-state","type-safe","typescript"],"created_at":"2024-11-17T03:19:38.621Z","updated_at":"2025-04-23T14:27:00.036Z","avatar_url":"https://github.com/zaaack.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Immuter\n\nAn immutable react/redux state update helper, easily handle nested state object with less code.\n\n\n[![Build Status](https://travis-ci.org/zaaack/immuter.svg?branch=master)](https://travis-ci.org/zaaack/immuter) [![npm](https://img.shields.io/npm/v/immuter.svg)](https://www.npmjs.com/package/immuter) [![npm](https://img.shields.io/npm/dm/immuter.svg)](https://www.npmjs.com/package/immuter)\n\n\n## Why\n\nFacebook's Immutable.js is too heavy, seamless-immutable is lite and simple, and backwards-compatible with normal Arrays and Objects. But the way to update is not friendly enough for me, I have to write too much code for updating state. I also tried something like dot-prop-immutable, object-path-immutable, timm, updeep, update-immutable, etc. they all are good, but neither of them's DX is good enough for me, so I create this one based on all benefits of these.\n\n## Install\n\n```sh\nnpm i immuter\n\n# or\n\nyarn add immuter\n```\n\n### More Code Example:\n```js\nimport { Struct } from 'immuter'\n\nlet struct = Struct({\n  title: {\n    zh: '哈利·波特与魔法石',\n    en: 'Harry Potter and the Philosopher\\'s Stone',\n  },\n  author: 'J. k. rowling',\n  tags: ['novel', 'magic'],\n})\n\nconst struct1 = struct.clone(struct =\u003e {\n  struct.author = 'New Author'\n  struct.title.en = 'New Title'\n  // return struct // return or not\n}) // Clone struct, it will only change modified part to optimize performance.\n\nstruct.author === 'J. k. rowling' // true\nstruct2.author === 'New Author' // true\n\nStruct.isStruct(struct) // true\n```\n\n## Demo\n\n### Simple mutation method\n```js\n\nimport Immuter from 'immuter'\n// or import { bindObj, binComp, get, set, update, del } from 'immuter'\nconst book = {\n  title: {\n    zh: '哈利·波特与魔法石',\n    en: 'Harry Potter and the Philosopher\\'s Stone',\n  },\n  author: 'J. k. rowling',\n  tags: ['novel', 'magic'],\n}\n\nlet titleEn\nlet bookLite\nlet newBook = book\n\n\n// get the English title\ntitleEn = Immuter.get(book, 'title.en')\n// or\ntitleEn = Immuter.get(book, ['title', 'en'])\n// return: Harry Potter and the Philosopher\\'s Stone\n\n// multiple get\nbookLite = Immuter.get(book, {\n  'title': 'title.en',\n  'author': 'author',\n}, {\n  'type': 'book',\n})\n// return {\n//  title: 'Harry Potter and the Philosopher\\'s Stone',\n//  author: 'J. k. rowling',\n//  type: 'book'\n// }\n\n// set the English title\nnewBook = Immuter.set(newBook, 'title.zh', '新标题!')\n// or\nnewBook = Immuter.set(newBook, ['title', 'en'], 'New title!')\n// return:  {\n//   title: {\n//     zh: '新标题!',\n//     en: 'New title!',\n//   },\n//   author: 'J. k. rowling',\n//   tags: ['novel', 'magic'],\n// }\n\n\n// set array item\nnewBook = Immuter.set(newBook, 'tags[0]', 'New tag')\n\n// update array, update is almost like the set, except the value is a function to update value,\n// note this function should be pure!\nnewBook = Immuter.update(book, 'tags', tags =\u003e tags.concat(['UK']))\n// return:  {\n//   title: {\n//     zh: '新标题!',\n//     en: 'New title!',\n//   },\n//   author: 'J. k. rowling',\n//   tags: ['New tag', 'magic', 'UK'],\n// }\n\n\n// multiple set\nnewBook = Immuter.set(newBook, {\n  'title.en': 'New Title!',\n  'author': 'New Author!'\n})\n\n\n// multiple update\nnewBook = Immuter.update(newBook, {\n  'title.en': title =\u003e title + ' (Original Edition)',\n  'author': author =\u003e author.toUpperCase(),\n  'tags': tags =\u003e tags.concat(['UK']),\n})\n\n// multiple delete\nnewBook = Immuter.delete(newBook, {\n  'title.zh': true, // this would be removed\n  'author': false, // this won't\n  'tags': false, // this won't, too\n})\n```\n\n### Advance\n\n#### bindObj\n```js\n\n\nimport Immuter from 'immuter'\n\nconst book = {\n  title: {\n    zh: '哈利·波特与魔法石',\n    en: 'Harry Potter and the Philosopher\\'s Stone',\n  },\n  author: 'J. k. rowling',\n  tags: ['novel', 'magic'],\n}\nlet newBook = book\nconst immuBook = Immuter.bindObj(newBook)\nconst titleEn = immuBook.get('title.en')\nnewBook = immuBook.set('title.en', 'New title!')\n\nimmuBook.set('author', 'J.K')\nnewBook = immuBook.getObj()\n```\n#### bindComp\n\nUsing bindComp decorator to bind a React Component, with flowtype.\n\n```js\nimport { Component } from 'react'\nimport Immuter from 'immuter'\nimport type { ImmuterGet, ImmuterSet, ImmuterUpdate, ImmuterDel } from 'immuter'\n\ntype State = {\n  title: {\n    zh: string,\n    en: string,\n  },\n  author: string,\n  tags: Array\u003cstring\u003e,\n}\n\n@Immuter.bindComp()\nclass CompA extends Component {\n  get: ImmuterGet\u003cState\u003e\n  set: ImmuterSet\u003cState\u003e\n  update: ImmuterUpdate\u003cState\u003e\n  del: ImmuterDel\u003cState\u003e\n  delete: ImmuterDel\u003cState\u003e\n  state: State = {\n    title: {\n      zh: '哈利·波特与魔法石',\n      en: 'Harry Potter and the Philosopher\\'s Stone',\n    },\n    author: 'J. k. rowling',\n    tags: ['novel', 'magic'],\n  }\n\n  componentDidMount() {\n    this.update('title.en', title =\u003e title + ' (Original Edition)')\n      .then((state) =\u003e {\n        // do what you want in setState callback\n      })\n  }\n}\n\n```\n\n\n## API\n\n### Immuter.get: \u003cT: Object\u003e(obj: T, string | Array\u003cstring\u003e, defaults: any) =\u003e any\nGet a deep property by dot path or array path\n\u003e Note: get wouldn't deep clone result for performance issues, just make sure all your modify operations are using immuter :).\n\n\n### Immuter.get\u003cT: Object\u003e(obj: T, path: { [string]: string | Array\u003cstring\u003e }, defaults: { [string]: any }) =\u003e { [string]: any }\nGet deep properties by an Object with custom key.\n\n### Immuter.set\u003cT: Object\u003e(obj: T, string | Array\u003cstring\u003e, value: any) =\u003e T\nSet a deep property by dot path or array path\n\n### Immuter.set\u003cT: Object\u003e(obj: T, pathValueMap: { [string | Array\u003cstring\u003e]: any }) =\u003e T\nSet deep properties by an Object with Path key\n\n### Immuter.update\u003cT: Object\u003e(obj: T, string | Array\u003cstring\u003e, updater: (val: any) =\u003e any) =\u003e T\nMostly like set, except passing a function to update value\n\n### Immuter.update\u003cT: Object\u003e(obj: T, pathUpdaterMap: { [string | Array\u003cstring\u003e]: (val: any) =\u003e any }) =\u003e T\nMulti update with an  path: updater map\n\n### Immuter.bindObj\u003cT: Object\u003e(obj: T, chain: boolean = false): ImmuterWrapper\u003cT\u003e\nThis function will return an ImmuterWrapper instance with all functions above as it's methods, and bind the obj inside, so you don't need to pass obj.\n  * chain: default false, modify method would return the modified object directly, otherwise would return this for chained calls.\n\n\n### Immuter.bindComp\u003cT: Object\u003e(ns: string | boolean=false, includes?: ?Array\u003cstring\u003e, excludes: Array\u003cstring\u003e = ['bindObj', 'bindComp'])\n\nThis function will bind immuter functions to React Component instance, you can get, set, delete or update component state directly with instance method `get`, `set`, `delete` or `update`.\n\n* ns: Whether using namespace, defaults is false, means immuter functions would mount on component instance, you can call `this.get('title.en')`, `this.set('title.en', 'Some title')`, etc. in your component. Or using an special object to mount, e.g ns='immter', so you should call like this: `this.get('title.en')`, `this.set('title.en', 'Some title')`\n* includes: An array of include methods, defaults is all.\n* excludes: An array of exclude methods, defaults is ['bindObj', 'bindComp'].\n\nThese methods will auto update state by `this.setState`, if you need to using setState's callback feature, don't worry, all modify methods will return a promise, so you can even using async/await with it!\n\n## Exported flow types for bindComp\n```js\nexport type ImmuterGet = (path: GetPath, defaults: *) =\u003e *\nexport type ImmuterSet = \u003cState\u003e(path: SetPath, value: *) =\u003e State\nexport type ImmuterUpdate = \u003cState\u003e(path: UpdatePath, fn?: Updater) =\u003e State\nexport type ImmuterDel = \u003cState\u003e(path: DelPath) =\u003e State\n```\n\n## Licence MIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaaack%2Fimmuter","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fzaaack%2Fimmuter","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fzaaack%2Fimmuter/lists"}