Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Include common add, delete, and update patterns in README? #115

Closed
christiangenco opened this issue Mar 2, 2018 · 15 comments
Closed

Include common add, delete, and update patterns in README? #115

christiangenco opened this issue Mar 2, 2018 · 15 comments

Comments

@christiangenco
Copy link
Contributor

I've spent so long thinking about immutable ways to update objects and arrays that I had trouble wrapping my head around using mutating methods again, as is needed with immer.

It would've helped me get started faster if simple "best practice" examples similar to these were included in the docs:

import produce from "immer";

// object mutations
const todosObj = {
  id1: { done: false, body: "Take out the trash" },
  id2: { done: false, body: "Check Email" }
};

// add
const addedTodosObj = produce(todosObj, draft => {
  draft["id3"] = { done: false, body: "Buy bananas" };
});

// delete
const deletedTodosObj = produce(todosObj, draft => {
  delete draft["id1"];
});

// update
const updatedTodosObj = produce(todosObj, draft => {
  draft["id1"].done = true;
});

// array mutations
const todosArray = [
  { id: "id1", done: false, body: "Take out the trash" },
  { id: "id2", done: false, body: "Check Email" }
];

// add
const addedTodosArray = produce(todosArray, draft => {
  draft.push({ id: "id3", done: false, body: "Buy bananas" });
});

// delete
const deletedTodosArray = produce(todosArray, draft => {
  draft.splice(draft.findIndex(todo => todo.id === "id1"), 1);
});

// update
const updatedTodosArray = produce(todosArray, draft => {
  draft[draft.findIndex(todo => todo.id === "id1")].done = true;
});

...though I'm not positive draft.splice(draft.findIndex(todo => todo.id === "id1"), 1) is the best way to delete an item from an array mutably. This is a case where the immutable draft.filter(todo => todo.id !== "id1") seems less verbose.

@mweststrate
Copy link
Collaborator

Note that return draft.filter(todo => todo.id !== "id1") will work as well :) The findIndex will be more efficient though. But for efficiency reasons, I generally recommend not to store id-ed objects in an array in the first place. Will copy the section to the readme

mweststrate added a commit that referenced this issue Mar 6, 2018
@mweststrate
Copy link
Collaborator

Added, closing

@gpandurov
Copy link

In your examples, the delete of an element from array will fail if findIndex doesn't find any element. You will then remove element from the end of the array which is not the desired result.

@celmaun
Copy link

celmaun commented Feb 18, 2020

@mweststrate The examples are gone now. Please consider adding them to the documentation. The docs are very lacking in their current state.

@mweststrate mweststrate reopened this Feb 19, 2020
@Jarred-Sumner
Copy link

Jarred-Sumner commented Feb 22, 2020

One thing I'm not sure of: do deletes in nested arrays work within producer? It'd be great if the usage patterns gave an example for nested arrays. I'm not having any issues with Map, just arrays.

I keep getting this error:

Immer drafts cannot have computed properties

Given a state like this:

{
   positions: [ [1] ]
}

I want to delete [1], so that positions is an empty array ([]), producing the following state:

{
  positions: []
}

I've tried several variations of deleting objects from an array but I keep getting the same error:

Immer drafts cannot have computed properties

Example code:

const deletePosition = produce((schema, {id}) => {
  for (const row of schema.positions) {
    if (isArray(row)) {
      let index = row.indexOf(id);
      if (index > -1) {
        row.splice(index, 1);
      }

      if (isEmpty(row)) {
        schema.positions.splice(schema.positions.indexOf(row), 1);
      }
    }
  }
})

I also tried:

  • re-creating the array
  • using map

But, it still results in the same error.

@ilhammeidi
Copy link

Amazing

@acefei
Copy link

acefei commented May 19, 2020

Is there any best practice for updating sub-items?
eg:

// object mutations
const todosObj = {
  id1: { done: false, body: "Take out the trash", item1: 1, item2: 2 },
  id2: { done: false, body: "Check Email", item1: 1, item2: 2 }
};

// update
const newData = {item1: 3, item2: 4}
const updatedTodosArray = produce(todosArray, draft => {
  const oldData = draft[draft.findIndex(todo => todo.id === "id1")];
  draft[draft.findIndex(todo => todo.id === "id1")] = {...oldData, ...newData}
});

@mweststrate
Copy link
Collaborator

yes, if you need a spread in immer, you are probably missing the point :)

const item = draft.find(todo => todo.id === "id1");
Object.assign(item, newData)

@mweststrate
Copy link
Collaborator

sorry, that came out wrong :'). What I wanted to point at, if you need a spread, that is generally a good flag that it can probably expressed in a simpler way when using immer.

@h0jeZvgoxFepBQ2C
Copy link

Pleaaaasee add this 👍 👍 👍

mweststrate added a commit that referenced this issue Jun 10, 2020
* Introduced `current`, which takes a snapshot of the current state of a draft and finalizes it (but without freezing). Current is a great utility to print the current state during debugging (no Proxies in the way), and the output of current can also be safely leaked outside the producer. Implements #441, #591
* [BREAKING CHANGE] getters and setters are now handled consistently: own getters and setters will always by copied into fields (like Object.assign does), inherited getters and setters will be left as-is. This should allow using Immer directly on objects that trap their fields, like down in Vue or MobX. Fixes #584, #439, #593, #558
* [BREAKING CHANGE] produce no longer accepts  non-draftable objects as first argument
* [BREAKING CHANGE] original can only be called on drafts and will throw otherwise (fixes #605)
* [BREAKING CHANGE] non-enumerable and symbolic fields will never be frozen
* [BREAKING CHANGE] the patches for arrays are now computed differently to fix some scenarios in which they were incorrect. In some cases they will be more optimal now, in other cases less. Especially splicing / unshifting items into an existing array might result in a lot of patches. Fixes #468
* Improved documentation in several areas, there is now a page for typical update patterns and a separate page on how to work with classes. And additional performance tips have been included. Fixes #457, #115, #462
* Fixed #462: All branches of the produced state should be frozen
* Fixed #588: Inconsistent behavior with nested produce
* Fixed #577: Immer might not work with polyfilled symbols
* Fixed #514, #609: Explicitly calling `useProxies(false)` shouldn’t check for the presence of Proxy.
@mweststrate
Copy link
Collaborator

Sorry for ignoring this issue so long, it always bungled at the end of my to-do list. There is now a dedicated page again for mutable updates! https://immerjs.github.io/immer/docs/update-patterns

@Dulanjala007
Copy link

import produce, {original} from "immer"; or import {original, produce} from "immer" // docs shows both, ???

// delete (if order of array doesn't matter)
const deletedTodosArray = produce(todosArray, draft => {
  const data= original(draft); // for large data sets
  const index = data.findIndex(todo => todo.id === "id1");
  draft[index] = draft[data.length - 1]; // much faster than splice
  draft.pop();
});

@mweststrate
Copy link
Collaborator

mweststrate commented Jun 25, 2020 via email

@IgPugliese
Copy link

does .map should work?

@mweststrate
Copy link
Collaborator

Please refrain from comment on closed issues.

@immerjs immerjs locked and limited conversation to collaborators Jul 21, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

10 participants