-1

I am trying to implement a simple optional chaining state update. What I want is for the items to ONLY be changed IF the item is defined/exists; if not/is undefined, I want the item to keep the previous state (e.g userID should remain = 2 if not updated).

To test this out I created an object with three variables:

const userObj = {
  firstName: "",
  lastName: "",
  userID: 2,
};

Then I created a function to update state:

const updateState = (item) => {
  return {
    userObj.firstName = item?.firstName,
    userObj.lastName = item?.lastName,
    userObj.userID = item?.userID,
  };
};

Finally I pass the item which contains only one item to update (firstName) and I call the function:

const item = {
  firstName: "None",
};

console.log(updateState(item));

The output:

 userObj.firstName = item?.firstName,
           ^

SyntaxError: Unexpected token '.'

But when I hover over userObj I can see its properties:

enter image description here

Arcanus
  • 642
  • 5
  • 20
  • Does this answer your question? [How to merge two objects, overriding null values?](https://stackoverflow.com/questions/56188121/how-to-merge-two-objects-overriding-null-values) – SoluableNonagon Feb 04 '22 at 20:07
  • What’re you trying to return? A fresh object, or do you simply want to update the `userObj` object? – Terry Feb 04 '22 at 20:09
  • I will user this for updating Redux state which is immutable, but I just want to understand how I can update state regardless of mutability. – Arcanus Feb 04 '22 at 20:15

3 Answers3

1

You get this error because what you are trying to return is an object with dots in its keys.

You're mixing assignment and object creation. In addition item?.firstName only helps with the case that item is null or undefined in a sense that it fails fast instead of throwing an exception because you can't access null.firstName.

For this kind of default you'd have to do something along the lines of typeof item.firstName === 'string' ? item.firstName : userObj.firstName.

To pull everything together:

const func = (value, fallback) => (
  typeof value === typeof fallback
    ? value
    : fallback
);

const updateState = (item) => {
  userObj.firstName = func(item?.firstName, item.firstName);
  userObj.lastName = func(item?.lastName, item.lastName);
  userObj.userID = func(item?.userID, item.userId);

  return userObj;
};

Note however that this function will mutate userObj.

Edit: default was a poor naming choice.

Marces Engel
  • 685
  • 6
  • 6
1

You can do it this way, using javascript object destructuring

const updateState = (item) => {
 return {
   ...userObj,
   ...item,
 };
};

so it will only update the key and value that was passed, i hope the question is not on typescript

Paulos Ab
  • 319
  • 4
  • 16
0

You can use null coalescing in conjunction with optional chaining:

const updateState = item => ({
  userObj.firstName = item?.firstName ?? userObj.firstName ,
  userObj.lastName  = item?.lastName  ?? userObj.lastName  ,
  userObj.userID    = item?.userID    ?? userObj.userId    ,
});

You could use the spread operator:

const updateState = item => {
  userObj = { ...userObj, ...item };
  return userObj;
}

Or you can use lodash's defaults() function:

const _ = require('lodash');

const updateState = item => {
  userObj = _.defaults(userObj, item)
  return userObj;
}

Or... if you really want to mutate the state object, rather than creating a new one and replacing it, roll your own, similar:

const updateState = item => {
  for (const key in item ) {
    const hasValue = key != null && key != undefined && key != NaN ;

    if ( hasValue ) {
      userObj[prop] = item[prop];
    }

  }
}

There is, as they say, more than one way to skin a cat.

[Edited: Add explanation of the spread operator]

The spread operator,

const obj = { ...obj1, ...obj2, . . . , ...objN };

is somewhat akin to calling a function like this:

const obj = mergeObjects( obj1, obj2, . . ., objN );

where mergeObjects() is defined as:

function mergeObjects(...objects) {
  const mergedObject = {};

  for (const obj of objects ) {
    for (const key in obj ) {
      mergedObject[key] = item[key];
    }
  }

  return mergedObject;
}

Or perhaps a better explanation might be done using Object.assign(). One could say that an expression like:

const obj = {
  
  prop1: 'a' ,
  prop2: 'b' ,
  
  ...obj1    ,
  
  prop3: 'c' ,
  prop4: 'd' ,
  
  ...obj2    ,
  
  prop5: 'e' ,
  prop6: 'f' ,
  
  ...obj3    ,
  
}

is the equivalent of this:

const obj = Object.assign( {},
  {
    prop1: 'a' ,
    prop2: 'b' ,
  },
  obj1 ,
  {
    prop3: 'c' ,
    prop4: 'd' ,
  } ,
  obj2 ,
  {
    prop5: 'e' ,
    prop6: 'f' ,
  } ,
  obj3 ,
);
Nicholas Carey
  • 71,308
  • 16
  • 93
  • 135
  • Null coalescing did not work for some reason. Got the same error. The spread operator worked perfectly. I understand that it copies both objects, but how does it update each key correctly? – Arcanus Feb 04 '22 at 20:33