Brian Ogden
Brian Ogden

Reputation: 19212

Correctly updating Object in React.js State without using spread operator

The spread operator ... is an experimental level feature according to Visual Studio or ReSharper Intellisense and I am using ECMAScript 2015: enter image description here

I am using Babel and I see the spread operator in the transpiled source: enter image description here

So I trying to replace spread operator functionality for updating on object in React state with Object.assign.

Use the spread operator I can update the state of my object with:

setInputs(prevInputs => ({ ...prevInputs, [name]:value}));

This works great. Now I want to remove the spread operator to maximize cross browser compatibility.

Unfortunately when I try and update a value change to a property in my state object using other methods I get the error:

A component is changing a controlled input of type text to be uncontrolled. Input elements should not switch from controlled to uncontrolled

I understand what this error message means, what I cannot understand is what is different about my object update, compared to the spread operator, here is the ways I have tried to replace the spread operator:

if (value) {
    //try to update state with value that is not undefined
    setInputs(prevInputs => Object.assign({}, prevInputs)[name] = value);

    //another try to update state with value that is not undefined
    setInputs(prevInputs => prevInputs[name] = value);
}

I do not understand what is going on here, the spread operator is creating a new object:

const originalObj = {
    name: '',
};
const spread = {...originalObj};
console.log('is originalObj === spread', originalObj === spread); //no they are not

How is my Object.assign way of replacing spread not closing mimicing the spread operator object creation?

Upvotes: 1

Views: 1137

Answers (2)

cjbt
cjbt

Reputation: 344

Looks like the spread operator is browser compatible according to babel itself.

enter image description here

Upvotes: 1

CertainPerformance
CertainPerformance

Reputation: 370779

To mimic spread and assign a new property to the object, you need to

(1) Create a shallow copy of the original object

(2) Assign a new value to that copied object

(3) Return the copied object

When you do

setInputs(prevInputs => Object.assign({}, prevInputs)[name] = value);

you're creating a shallow copy, but then you're returning the assignment, so the return value is the value of the assignment, not the copied object. The above code is equivalent to

setInputs(prevInputs => value);

Best to avoid using assignments in expressions - they're almost never what you want.

With

setInputs(prevInputs => prevInputs[name] = value);

you're doing something a bit similar: you're mutating the existing prevInputs object, and returning the value. It's equivalent to

setInputs(prevInputs => {
  prevInputs[name] = value;
  return value;
});

To achieve the shallow copy, set a value on the copied object, and return it, do:

setInputs(prevInputs => {
  const newInputs = Object.assign({}, prevInputs); // Shallow copy
  newInputs[name] = value; // Assign new value
  return newInputs; // Return copied object
});

Or, equivalently:

setInputs(prevInputs => Object.assign({}, prevInputs, {[name]: value }));

The above works because Object.assign returns the new object (the first parameter).

Keep in mind that this avoids object spread syntax (which is ES2018), resulting in the code being compatible with ES2015. To make the code compatible with ES5 (ES2009) will require a lot more work. Either way, to make your code compatible, it's probably best to use Babel to take care of it automatically - this keeps your source files nice and readable, while permitting obsolete browsers to understand the transpiled code.

Upvotes: 2

Related Questions