Conditionally Change Object in JavaScript

Here's how to conditionally update an object in JavaScript.

Best way

This is the best method for conditionally updating an object for two reasons:

  1. It's copy-on-write, avoiding mutation of the object.

  2. It uses the most expressive syntax available to accomplish that.

function update(obj) {
  return {
    ...obj,
    ...(condition() && { b: 2 })
  }
}

Copy-on-write. One expression in the return. Returns an object made of two sources: a copy of the original object and, conditionally, the b member.

Ship it. You can stop there.

Or, a cautionary tale about... mutation!

Mutation

If you have some condition that you need to check before updating a JavaScript object, you can do so by mutating the existing object, such as:

function update(obj) {
  if (condition()) {
    obj.b = 2
  }
  return obj
}

Is that all that bad? Yes.

If you're mutating local state, fine. But if you get in the habit, you'll cross the line. Stay in the immutable habit. This almost looks like local state, but it is not. obj is passed as an argument to the function by reference, as objects are in JavaScript. That means that when you're modifying obj, you modifying the original too.

Let's expand the bit of code to demonstrate:

function condition() {
  return true;
}

function update(obj) {
  if (condition()) {
    obj.b = 2;
  }
  return obj;
}

const thing = { a: 1, b: 1 };
const updatedThing = update(thing);

console.log("thing", thing);
console.log("updatedThing", updatedThing);

// yields
// thing { a: 1, b: 2 }
// updatedThing { a: 1, b: 2 }

Now thing.b is also 2, and this likely is unintended.

Spread with conditions

So, copy the object first. The spread operator helps there (ie, { ... obj }).

And set a field conditionally. The spread operator can be used there too. Check these experiments:

node
> { a: 1, ...(true && { b: 2 }) }
{ a: 1, b: 2 }
> { a: 1, ...(false && { b: 2 }) }
{ a: 1 }

And if we applied our copy-on-write solution to the "thing" example above, we would get:

thing { a: 1, b: 1 }
updatedThing { a: 1, b: 2 }

And that's a good thing TM.