Home > Back-end >  Can't implement simple optional chaining
Can't implement simple optional chaining

Time:02-05

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

CodePudding user response:

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.

CodePudding user response:

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

CodePudding user response:

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.

CodePudding user response:

So to keep old state you need to write logic in for that.

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

// shorthand should be something like this:
// Creates a new object {}
// merges in userObj
// merges in item
// this will ignore null values but likely keep empty strings

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

  •  Tags:  
  • Related