8

I'm using es6 object destructuring to provide default parameters to a function.

function mapStateToProps({ shops: { cakeShop: {}, pieShop: {} }) {
  return {
    CakeShopName: shops.cakeShop.Name,
    PieShopName: shops.pieShop.Name
  }
}

The problem with the above is that if I call

mapStateToProps({})

The code throws Cannot read property 'Name' of undefined. The nested objects on shops are not set to their default values and the code has a null reference.

How can I make sure that the nested objects within shops are being set to the right default value, even if shops itself is defined?

glcheetham
  • 973
  • 8
  • 23
  • I don't think you can do that with destructuring, it works only if you are sure... – n00dl3 Mar 17 '17 at 12:01
  • Sound like you were confusing destructuring with default values. Your syntax destructures an argument object, but doesn't actually introduce any parameter identifiers. – Bergi Mar 17 '17 at 12:57

3 Answers3

14

Sound like you were confusing destructuring with default values. Your syntax destructures an argument object, but doesn't actually introduce any parameter identifiers. There is no shops variable in your function scope.

I'll assume that you actually wanted to introduce cakeShop and pieShop variables, and provide them with defaults. To do that, you'd write

function mapStateToProps({ shops: { cakeShop = {}, pieShop = {} }) {
// short for             { shops: { cakeShop: cakeShop = {}, pieShop: pieShop = {} }) {
// parameter names (that will be bound):      ^^^^^^^^                ^^^^^^^
  return {
    CakeShopName: cakeShop.Name,
    PieShopName: pieShop.Name
  }
}

You might also use

function mapStateToProps({ shops: { cakeShop: {name: CakeShopName} = {}, pieShop: {name: PieShopName} = {} }) {
  return {CakeShopName, PieShopName};
}
Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • This is right. As far as I can see, this is the same as @estus answer but with cleaner syntax (without the extra two `= {}` at the end?) – glcheetham Mar 17 '17 at 13:45
  • @glcheetham [Those defaults are needed](http://stackoverflow.com/q/34275971/1048572) when you want to be able to call the function with an object that has no `shops` property or without any argument at all. – Bergi Mar 17 '17 at 14:05
7

To handle default values for nested objects, it should be

function mapStateToProps({ shops: { cakeShop = {}, pieShop = {} } = {} } = {}) {
...
}
Estus Flask
  • 206,104
  • 70
  • 425
  • 565
  • This works but now `shops` is undefined within the function scope. What if I need to access alot of props on `shops`? – glcheetham Mar 17 '17 at 12:16
  • The code in the question doesn't explain that. cakeShop, etc can be used as `cakeShop.Name`. If you really need `shops` then don't use param destructuring and do `const { cakeShop = {}, pieShop = {} } = shops` instead. You cannot have both shops and cakeShop from param destructuring at the same time. – Estus Flask Mar 17 '17 at 12:48
  • you can: `{shops, shops: { cakeShop = {}, pieShop = {} } = {} } = {}` - keep in mind that if e.g. `cakeShop` is `null`, `false` or `0` your default will be set – Dude Sep 14 '18 at 11:46
  • 1
    @Dude Defaults are applied to `undefined` only. It's the opposite, if a value can be null then defaults won't be applied, if they are desirable then destructuring is unsuitable in this case. – Estus Flask Sep 14 '18 at 12:23
1

I know this question is about destructuring but for anyone interested I'm leaving another option here using lodash or underscore:

function mapStateToProps(shops) {
  _.defaultsDeep(shops, {
    cakeShop: {
      Name: "Kiki's CakeShop"
    }, 
    pieShop: {}
  })

  return {
    CakeShopName: shops.cakeShop.Name, // defaults to "Kiki's CakeShop"
    PieShopName: shops.pieShop.Name // undefined if not specified
  }
}
JohnnyQ
  • 4,839
  • 6
  • 47
  • 65