Create the next immutable state by mutating the current one

Overview

Immer

npm Build Status Coverage Status code style: prettier OpenCollective OpenCollective Gitpod Ready-to-Code

Create the next immutable state tree by simply modifying the current tree

Winner of the "Breakthrough of the year" React open source award and "Most impactful contribution" JavaScript open source award in 2019

Contribute using one-click online setup

You can use Gitpod (a free online VS Code like IDE) for contributing online. With a single click it will launch a workspace and automatically:

  • clone the immer repo.
  • install the dependencies.
  • run yarn run start.

so that you can start coding straight away.

Open in Gitpod

Documentation

The documentation of this package is hosted at https://immerjs.github.io/immer/

Support

Did Immer make a difference to your project? Join the open collective at https://opencollective.com/immer!

Release notes

https://github.com/immerjs/immer/releases

Comments
  • Map & Set support (v5)

    Map & Set support (v5)

    Try v5.0.0 today!

    npm install immer@next
    

    Tasks

    • [x] ES6 Map / Set support with setUseProxies(true)
    • [x] Ensure onDelete/ onAssign hooks work with ES6 maps/sets (and write tests)
    • [x] Ensure object keys can be used with ES6 maps
    • [x] ES6 Map / Set support in ES5 mode
    • [x] Update typings for ES6 maps/sets
    • [x] Update docs website
    • [x] fix build
    • [x] process review comments, create follow up tasks, also for typings
    • [x] verify build size: 4.59 -> 6.28 gzipped, minified
    • [x] check for duplicate code in traps, source fn
    • [x] check for unnecessary closures in traps etc
    • [x] how to freeze map / set?
    • [x] check perf impact -> no significant changes
    released 
    opened by aleclarson 42
  • Idea: introduce limited support for immutable, cloneable classes

    Idea: introduce limited support for immutable, cloneable classes

    We could introduce limited support for classes, by preserving the prototype of an object when cloning objects. (See als the comments here)

    There are a few potential gotchas however

    • Not all data structures can safely be cloned by just preserving the prototype (e.g. most built-in types like, Map, Buffer, HTMLElement etc etc)
      • So, cloneable classes should be marked specially, like class X extends ImmerCloneable or X[immerCloneable] = true
    • It becomes more unclear what kind of data structures can be cloned, and what not
    • Object.create needs polyfilling?
    proposal 
    opened by mweststrate 38
  • [v1.10.3] Immutable<T> broke my codebase

    [v1.10.3] Immutable broke my codebase

    Hey, there! I'm the guy that provided the original mapped type for removing readonly modifiers in TypeScript over in #161.

    It's awesome to see this library gain so much traction since then, though the typings have also evolved to the point where I can barely recognize them. The latest patch release of immer broke a library I wrote (my tests pass using [email protected] but not [email protected]). It appears to be purely a typing thing, but I'm having a heck of a time resolving the issues. I was hoping somebody who has worked on the typings more recently than I have could give some pointers/feedback/maybe a PR against my project (or maybe we might determine that the typings are broken and not my usage and then we can fix the problem here).

    The issue Greenkeeper opened against my repo can be seen here: https://github.com/knpwrs/redux-ts-utils/issues/1

    This is the code that was working with [email protected]:

    export default function handleAction<S, AC extends TsActionCreator<any> = any>(
      ac: AC,
      re: TsReducer<S, ReturnType<AC>>,
      s?: S,
    ): Reducer<S, ReturnType<AC>> {
      return produce((draft: Draft<S>, action: ReturnType<AC>) => {
        if (action.type === ac.type) {
          re(draft, action);
        }
      }, s);
    }
    

    Lots of red all over the place. Here's where I've gotten so far with [email protected]:

    export default function handleAction<S, AC extends TsActionCreator<any> = any>(
      ac: AC,
      re: TsReducer<S, ReturnType<AC>>,
      s?: S,
    ): Reducer<S, ReturnType<AC>> {
      return produce<S, Draft<S>, [ReturnType<AC>]>((draft, action) => {
        if (action.type === ac.type) {
          re(draft, action);
        }
      }, s);
    }
    

    Now the only thing that is red is that second-to-last line where I'm passing s as the default value to produce. TypeScript complains:

    Argument of type 'S | undefined' is not assignable to parameter of type 'S'.
      Type 'undefined' is not assignable to type 'S'.
    

    Nothing I'm trying is resolving the issue (even type casting or the dreaded ! operator which results in more errors). I want s to remain an optional parameter.

    Any ideas?

    question released typescript 
    opened by knpwrs 35
  • Idea: Ignore return value of producer

    Idea: Ignore return value of producer

    Currently the return value (if any) matters a lot, to be able to replace the current state. So there are quite some rules

    1. Return undefined (or no return at all), will assume the draft is the state to be returned
    2. Return any value (except draft or undefined), will replace the state with the returned value

    This cause a few tricky cases:

    • Because of 1, we cannot replace the current state with undefined, by simple returning undefined. For that reason, nothing has to be returned
    • Because of 2, it is easy to accidentally replace the state, for example in produce({ x: 1}, draft => draft.x = 2) will produce 2 as the next state, not { x: 2 }. (Since assignment is statements return their right hand value, the result of the function is 2, not undefined. To avoid this, we have to write either draft => { draft.x = 2 } or draft => void draft.x = 2.

    We could design the API differently, and always by default ignore the return value of a producer, regardless what the return value is. This fixes point 2, where we accidentally replace the state.

    To be able to replace the state, we could have a dedicated internal indicating that we intend the replacement: return replace(3). This also solves 1), as undefined has no longer to be treated specially, and we can just return replace(undefined).

    Note that we could even detect a replace call without return and warn the user.

    Beyond that, we can possible warn about return a value, but not updating the draft (probably replace was intended)

    So, in summary:

    import { produce, replace } from "immer"
    
    produce({ x: 1 }, draft => draft.x = 2) 
    // returns { x: 2 }
    
    produce({x : 1}, draft => replace(2))
    // returns 2
    
    produce({x : 1}, draft => replace(undefined))
    // returns undefined
    
    produce({ x: 1 }, draft => {
       return replace(3)
    })
    // returns 3
    
    produce({ x: 1 }, draft => {
       draft.x = 2
       return 3
    })
    // returns { x: 2 }
    // The '3' is ignored entirely (and we can't really warn about it)
    

    Alternatively we could warn about non-undefined return values (fixing the last case) and forcing consumers to either use d => { ... } or d => void ... or d => replace(...) in producers.

    Edit: removed the warnings

    proposal breaking change 
    opened by mweststrate 34
  • Would it be possible to make produce's recursive noop optional?

    Would it be possible to make produce's recursive noop optional?

    This is a feature request and I'm more than happy to implement it myself if accepted.

    Background

    After v1.3.0, recursive produce calls result in a no-op. @mweststrate explained the pros and cons of this behavior here. We're building a project with Redux and have been taking advantage of immer to reduce boilerplate in our reducers. While optimizing our use of Redux by batching actions to avoid excessive calls to produce, we've run into an issue with this behavior.

    Issue

    To reduce boilerplate in our Redux reducers, we implement all of our reducers as impure reducers and make them pure by calling produce(reducer, initialState). Some of our reducers use higher order reducers (e.g., redux-undo). Because of Redux's contract, high order reducers will often make the assumption that reducers are pure. Unfortunately, after v1.3.0, produce(reducer, initialState) will only "purify" your reducer if you're not inside a produce call.

    Our current solution is to avoid calling produce recursively, which removes the ability to compose our reducers. @mweststrate's point (ii) in the comment is a great observation, but for us it's not an issue when composing reducers since Redux reducers are pure.

    Here's a small example showing the desired behavior:

    This is a sub-reducer that holds a slice of the entire Redux state. One might want to wrap it in a higher order reducer like redux-undo.

    // counter.js
    const reducer = (state?: State = initialState, action: Action): State => {
      switch (action.type) {
        case ActionTypes.INCREMENT:
          state.count += action.payload
          break
    
        case ActionTypes.DECREMENT:
          state.count -= action.payload
      }
    
      return state
    }
    
    export default produce(reducer, initialState)
    

    This is the root reducer which has a slice of state handled by counter.js. The counter reducer should be pure, but when it's called within a produce call, which it is, it loses it's purity.

    // reducer.js
    const reducer = (state?: State = initialState, action: Action): State => {
      switch (action.type) {
        case ActionTypes.TOGGLE:
          state.value = !state.value
      }
    
      state.counter = counter(state.counter, action)
    
      return state
    }
    
    export default produce(reducer, initialState)
    

    Instead, reducer.js has to be modified to avoid recursive produce calls:

     // reducer.js
     const reducer = (state?: State = initialState, action: Action): State => {
       switch (action.type) {
         case ActionTypes.TOGGLE:
           state.value = !state.value
       }
    
    -  state.counter = counter(state.counter, action)
       return state
     }
    
    -export default produce(reducer, initialState)
    +export default (state?: State, action: Action): State => {
    +  const nextState = produce(reducer, initialState)(state, action)
    +
    +  return { ...nextState, counter: counter(nextState.counter, action)
    +}
    

    Proposal

    Enable recursive calls of produce by default like pre-v1.3.0. Add a new method setRecursiveProduce (for lack of a better name) that would allow configuration of this behavior like post-v1.3.0.

    Would love to hear some thoughts on our approach and if other people would find something like this useful. If this is something the maintainers would accept, I would gladly submit a PR.

    proposal 
    opened by migueloller 32
  • Async recipes

    Async recipes

    Localized branch of #305, implements #302

    Remaining work:

    • [x] docs
    • [x] createDraft / finishDraft api
    • [x] typings create/finish Draft
    • [x] typings async produce
    released 
    opened by mweststrate 31
  • Version 1.9.1 Typescript issue with generics

    Version 1.9.1 Typescript issue with generics

    Hello again :D (hope, it doesn't get annoying)

    I've written a small function:

    const insertAtIndex = <T>(array: T[], index: number, elem: T): T[] => {
        return produce(array, draft => {
            draft.splice(index, 0, elem as any); // <= need "any" for this to work :(
        });
    }
    

    Without using as any I get:

    Argument of type 'T' is not assignable to parameter of type 'T extends ReadonlyArray<any> ? { [P in keyof T]: Draft<T>; }[keyof T] : DraftObject<T>'. ts(2345)
    

    This error only appears within this generic function in combination with Immer.

    These two cases work as expected:

    export const insertAtIndex = <T>(array: T[], index: number, elem: T) => {
        array.splice(index, 0, elem);
    }
    
    const array = [1,2,3];
    const elem = 4;
    const newArray = produce(array, draft => {
        draft.splice(42, 0, elem);
    })
    
    bug released typescript 
    opened by benneq 23
  • Skip key in shallowCopy

    Skip key in shallowCopy

    🚀 Feature Proposal

    It would be great if the user could control the shallowCopy in a way to tell what keys to skip during the process. The easiest thing would be if we could provide a predicate which returns bool at this specific part providing it the key and base.

    Motivation

    One of the possible use-cases for this might be for Frameworks such as Aurelia and it's state management plugin Aurelia Store to by-pass the built in observable trackers placed on objects by Aurelia.

    Example

    I've no idea how to register the predicate in order to consume it in the commons helper but maybe something along these lines:

    import { setSkipCopyPredicate } from "immer";
    
    function myPredicate(base, key) {
      return (key !== "FOOBAR")
    }
    
    setSkipCopyPredicate(myPredicate);
    
    proposal 
    opened by zewa666 22
  • immer 3.0

    immer 3.0

    • [x] Implement #308: no this
    • [ ] ~Implement #246, ignore return value, use return replace(value) for replacement~
    • [x] Merge #232: improved flow typings
    • [ ] ~Improve minification by using property mangling?~
    • [x] Drop support for Node 6
    work in progress released 
    opened by mweststrate 20
  • feat: support async recipes

    feat: support async recipes

    Drafts won't be revoked until the returned promise is fulfilled or rejected.

    The readme needs to be updated to reflect this. Anyone willing to contribute here?

    As suggested by @Yurickh in https://github.com/mweststrate/immer/issues/302#issuecomment-457839538

    opened by aleclarson 20
  • Idea: non-callback form of immer

    Idea: non-callback form of immer

    Occasionally I have cases where the callback based api of Immer is to limited, for example when I don't have explicit control over the flow or timing that drafts should be modified. For example in asynchronous processes where the draft should be updated bit by bit. In those cases I would like an api in which I can have more control, for example:

    import { createDraft, finishDraft } from "immer"
    
    const todo = { title: '', done: false }
    
    async function loadTodo() {
        const draftTodo = createDraft(todo)
        draftTodo.title = await fetchTitle()
        draftTodo.done = await fetchDone()
        todo = finishDraft(draftTodo)
    }
    
    proposal released 
    opened by mweststrate 18
  • The return type of the produce method will have an additional undefined

    The return type of the produce method will have an additional undefined

    🙋‍♂ Question

    A clear and concise description of what the question is. In general we recommend to use Stack Overflow for questions. Questions typically are processed with less priority.

    import { produce } from 'immer';
    
    const func = produce((state: Record<string, string> = {}): Record<string, string> => {
      return state;
    });
    

    In the above code, I expect the type of the func to be Record<string, string>, but the actual type check returns Record<string, string> | undefined, how can I avoid the undefined?

    image

    This problem only occurs in 9.x versions

    Link to repro

    https://codesandbox.io/s/immer-sandbox-forked-n1okz2?file=/src/index.ts

    Environment

    We only accept questions against the latest Immer version.

    • Immer version:
    • [ ] Occurs with setUseProxies(true)
    • [ ] Occurs with setUseProxies(false) (ES5 only)
    question 
    opened by JaneSu 0
  • TypeScript: programmatically updating multiple object fields in produce causes issues with index signature

    TypeScript: programmatically updating multiple object fields in produce causes issues with index signature

    🙋‍♂ Question

    I am unable to generate a correct type signature for a setter function that programmatically updates multiple fields in an Object Draft.

    I am looking for advice on how to combine TypeScript and Immer to create a correct signature (hopefully without using casting as).

    Link to repro

    I created a TypeScript playground where you can see the error reproduced.

    Environment

    "typescript": "~4.8.2" "immer": "^9.0.16",

    • Immer version: "^9.0.16"
    • [ ] Occurs with setUseProxies(true)
    • [ ] Occurs with setUseProxies(false) (ES5 only)
    question 
    opened by floroz 1
  • Immer overwrites properties

    Immer overwrites properties

    Trying to create alternative object build with produce, but everything comes down to immer overwriting properties every time in the loop: Original: Screen Shot 2022-12-09 at 4 01 45 PM Produce alternative: Screen Shot 2022-12-09 at 4 04 06 PM Can someone explain to me how to make alternative version to be working as one with spreads? Thank you!

    opened by yasinitskyi 5
  • Cannot import correctly under TypeScript Node 16 module resolution

    Cannot import correctly under TypeScript Node 16 module resolution

    🐛 Bug Report

    Under TypeScript Node 16 module resolution (has type: module in the package.json and moduleResolution: Node16 in tsconfig.json), this package cannot be imported correctly.

    image

    TypeScript thinks this package is a CommonJS package and therefore provides the synthetic export for the CommonJS module.

    To Reproduce

    import produce from 'immer'
    
    produce(() => {}, {})
    // TS: This expression is not callable.
    
    produce.default(() => {}, {})
    // TS ok, but runtime error: produce.default is not a function
    

    package.json

    { "type": "module" }
    

    tsconfig.json

    {
        "compilerOptions": {
            "moduleResolution": "Node16"
        }
    }
    

    Observed behavior

    Cannot be imported.

    Expected behavior

    Imported correctly.

    Environment

    • Immer version:
    • [x] I filed this report against the latest version of Immer
    opened by Jack-Works 1
  • fix: recursive issue with Draft and JSONTypes

    fix: recursive issue with Draft and JSONTypes

    fixes #839

    I have added test under __tests__/types/type-externals.ts.

    There are several things I have observed:

    • the default formatter setting in vscode is not set to prettier. I have changed that so I won't mess up your formatting).
    • there are some test failure from the previous PR
    • the project doesn't build in Windows: yarn build -> 'cp' is not recognized as an internal or external command
    • there are some circular dependency issues:
    Circular dependency: src\internal.ts -> src\types\types-internal.ts -> src\internal.ts
    Circular dependency: src\plugins\patches.ts -> src\immer.ts -> src\plugins\all.ts -> src\plugins\patches.ts
    Circular dependency: src\internal.ts -> src\types\types-internal.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\utils\common.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\utils\plugins.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\scope.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\finalize.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\proxy.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\immerClass.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\current.ts -> src\internal.ts
    Circular dependency: src\plugins\patches.ts -> src\immer.ts -> src\plugins\patches.ts
    Circular dependency: src\plugins\patches.ts -> src\immer.ts -> src\plugins\all.ts -> src\plugins\patches.ts
    Circular dependency: src\internal.ts -> src\types\types-internal.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\utils\common.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\utils\plugins.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\scope.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\finalize.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\proxy.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\immerClass.ts -> src\internal.ts
    Circular dependency: src\internal.ts -> src\core\current.ts -> src\internal.ts
    Circular dependency: src\plugins\patches.ts -> src\immer.ts -> src\plugins\patches.ts
    

    I think these should be fixed.

    Here is an video about the details: https://www.youtube.com/watch?v=PCRzWIubAEQ

    opened by unional 1
Releases(v9.0.17)
Owner
immer
Create the next immutable state tree by simply modifying the current tree
immer
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
Create a schema object to encode/decode your JSON in to a compact byte buffer with no overhead.

schemapack The fastest and smallest JavaScript object serialization library. Efficiently encode your objects in to compact byte buffers and then decod

null 442 Nov 26, 2022
Transmute one JavaScript string into another by way of mutating its AST. Powered by babel and recast.

equivalent-exchange Transmute one JavaScript string into another by way of mutating its AST. Powered by babel and recast. Features Can parse code usin

Lily Scott 51 Jul 9, 2022
⚡️The Fullstack React Framework — built on Next.js

The Fullstack React Framework "Zero-API" Data Layer — Built on Next.js — Inspired by Ruby on Rails Read the Documentation “Zero-API” data layer lets y

⚡️Blitz 12.5k Jan 4, 2023
A library for updating your immutable state in JavaScript applications.

ionic-bond A library for updating immutable states in JavaScript applications. Introduction This library is a very lightweight replacement for immer,

null 10 Nov 15, 2022
Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic.

Synergies Create a performant distributed context state by synergyzing atomar context pieces and composing reusable state logic. synergies is a tiny (

Lukas Bach 8 Nov 8, 2022
Download all Moodle files with one click. This is a Chrome extension built to save time and effort from downloading files manually one by one!

Moodle Downloader Extension Moodle downloader extension for Chrome. The extension is tested with both the TUM moodle and the official moodle demo. Not

Zhongpin Wang 8 Nov 15, 2022
Prefetch and sync state to client with one line of code, out-of-the-box

vue3-SSR-starter Prefetch and sync state to client with one line of code, out-of-the-box Features vue3 SSR vue-router we don't need vuex anymore one l

周子贤 36 Aug 28, 2022
Create an Apple-like one page scroller website (iPhone 5S website) with One Page Scroll plugin

#One Page Scroll 1.3.1 by Pete R. Create an Apple-like one page scroll website (iPhone 5S website) with One Page Scroll plugin Created by Pete R., Fou

Pete R. 9.6k Dec 31, 2022
⏰ Day.js 2KB immutable date-time library alternative to Moment.js with the same modern API

English | 简体中文 | 日本語 | Português Brasileiro | 한국어 | Español (España) | Русский Fast 2kB alternative to Moment.js with the same modern API Day.js is a

null 41.7k Dec 28, 2022
🕑 js-joda is an immutable date and time library for JavaScript.

js-joda is an immutable date and time library for JavaScript. It provides a simple, domain-driven and clean API based on the ISO8601 calendar.

null 1.5k Dec 27, 2022
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Dec 31, 2022
Immutable persistent data collections for Javascript which increase efficiency and simplicity.

Immutable collections for JavaScript Immutable data cannot be changed once created, leading to much simpler application development, no defensive copy

Immutable.js 32.4k Jan 7, 2023
⏰ Day.js 2kB immutable date-time library alternative to Moment.js with the same modern API

English | 简体中文 | 日本語 | Português Brasileiro | 한국어 | Español (España) | Русский Fast 2kB alternative to Moment.js with the same modern API Day.js is a

null 41.7k Dec 28, 2022
Uncensorable, immutable microblogging platform that is completely decentralized and does not rely on any centralized systems.

Zooko Truly decentralized, immutable and uncensorable microblogging Zooko is a working-example, proof-of-concept proving that you can have a decentral

Publius Federalist 152 Apr 20, 2022
Serverless for Web3, which is Immutable and Verifiable✅

Tender Layer 2 for IPFS / API Renderer for Web3 / Serverless for Ethereum Tender is dynamic content serving P2P Protocol for Ethereum. V1 Design A Cod

Hyojun Kim 23 Nov 18, 2022
Functional Programming with NestJS, Prisma. immutable, pure, stateless

Functional-NestJS Functional Programming with NestJS, Prisma. immutable, pure, stateless. 1. Introduction A production ready typescript backend reposi

y0on2q 40 Dec 6, 2022
Proof of concept: support immutable trpc servers using lambdas to ensure client/server compatibility

auto-versioned-trpc-aws-lambda Proof of concept to support an automatically versioned AWS Lambda running tRPC to ensure a somewhat graceful and automa

Kenneth Skovhus 5 Aug 30, 2022
Interplanetary Database: A Database built on top of IPFS and made immutable using Ethereum blockchain.

IPDB IPDB (Interplanetary Database) is a key/value store database built on top of IPFS (Interplanetary File System). Project is intended to be an MVP

turinglabs 8 Oct 6, 2022
A prototype on how web3 technology can enable us to build an open, immutable, reproducible, and permanently accessible scientific record.

Web3 Research A prototype on how web3 technology can enable us to build an open, immutable, reproducible, and permanently accessible scientific record

manveer 0 Nov 27, 2022