get method from the popular utility library
This person object has two properties, first name and last name. If you wanted to access any of them, for example to log them to the console or render them inside a component, you could do so like this.
This would, of course, work for last name as well, and if you try and access a property that doesn't exist, such as pets, you'll get undefined. This is quite useful, actually, because you can use the fact that it will give you undefined as a conditional.
For example, you could say if person.pets then inside this block you could render the UI. We'll just put an else block in place just to highlight when this is false.
Now you can see that it says, "Person has no pets." We've tried to access the pet's property. This returns undefined because it doesn't exist, which means we end up here.
Let's add a pet to this person to make this clearer. We'll say pets is an array, and inside there we just have one pet with the name Kitty. We could log person.pets. This seems to be working really well, but let's make this a bit more realistic.
Let's create another object inside here and say that pets is a property of that object instead. Now if you want to check your fifth person has pets we need to change these to instead have details.pets. You can see it's working just as it did before, but there's a very big difference here.
If we were to remove this details object all together and then look back at the conditional, so we have a person object, then we're accessing the details property, and the pets property on that. Hang on a minute. This may or may not exist.
This is where the problem lies, because if we log this to the console you can see that instead of getting this console.log line we get this error. This is a huge problem. For example, any code that's below this error will not run.
If I tried to say "after here" and run it again you can see we get the error. We don't get this part afterwards. If this is just a couple of lines inside your program the code that comes after is not going to run, and that's going to cause you huge problems.
To perform this conditional in a safer manner you would have to check each property one at a time as you descend into the object. This would become one conditional, and then we can say...
If we run this again you can see that now we do enter this block, person has no pets, and this line of code does in fact run. This works because the first conditional here fails, which means we never try to access .pets on details either here or inside here, and we end up inside the else block.
You can see with just one level of nesting this is already getting really verbose. The code is not nice to write or to read, and it's in a very defensive style.
It's also a lot work for what is essentially a simple task. We just want to do something if this person has pets, and luckily there are better ways to go about this.
Let's first look at a library called Lodash and its get method. I've installed the module through MPM so I can simply require it, and we'll say _get equals require lodash.gets. We can say const pets is equal to _get.
The first source is the underscore object so we're passing the person, and the second argument, as we can see highlighted in the editor there, takes an array or a string of paths. We can take this section here, provide it as a string, and this alone will do exactly the same as all of these conditionals.
If we were to just log pets here and comment out all of this for a moment you can see that we just get undefined. This is amazing, because even though details doesn't exist on this person trying to access pets on details will not cause that error to be thrown, and will not cause our program to crash.
This means that our previous conditional can be cleaned up now. We can instead just say "if pets." Behind the scenes lodash is looking at each of these items, checking if they exist on the previous object, and if any of these lookups fail it's just returning undefined instead of throwing that error.
This gets even more useful when you realize that you can even use this technique for accessing items within an array. Let's go back to this person having some pets. If we wanted to access the very first pet and look at its name property with lodash get, it's as simple as this.
We can use the square bracket syntax to access the first element in the pets array, and then we can access its name property. Change this to name and then log it. You can see that you're able to perform some really complicated lookups all without the danger of an error being thrown.
As mentioned earlier you can also provide an array here instead of a string so you can split these up manually, and you get the exact same results. This technique is useful in situations where you find yourself repeating the first couple of paths.
For example if you always descend into pets you could save that as a name space. You could say name space is details pets, and then anytime you want to access something within pets you could say namespace.com.cat, and then just put your paths inside there so you go zero and name. Again you get the same results.
The final thing to cover, which is possibly my favorite feature, is the ability to provide a default value that will be used if the path lookup returned undefined. Let's give this person a gallery instead. Gallery is just an array with three images, and we can access that gallery by just providing the string gallery.
If we log it to the console you can see we have all three images. Now let's say we only want to render the gallery if there are images inside the array.
A common thing to do is just to check the length in this way, and then we can say "render the gallery," and if it was an empty array we'll be inside the else block and we can say "no gallery."
This seems to work. We're rendering the gallery, and there are three items. But watch what happens if for some reason this property doesn't exist at all. If we run it we can see that we're back to the situation we had previously where we're getting this error thrown, and any code below here would not run.
It's because this has returned undefined, because this doesn't exist, and length is not a property on an undefined value, which is why it stays here.
To solve this the manual way we would have to check first if gallery, check if it's an array, and then check if it has a length. Then we can render the gallery. It's worked again. This time we're not rendering it.
But we're right back and square one at this point with all of these conditionals and our defensive code. This is where the default value comes in handy. Let's go back to this.
With the get method you can provide a third argument which is the default value. In this case if a gallery exists that will be retuned, which will be an array, but if this returns undefined don't give me undefined. Instead give me this empty array on which we can then call .length and end up here.
And, of course, this becomes even more useful if gallery is nested somewhere else. Maybe it's inside media and inside photos, or something like that. This whole thing will still work. This is preventing any errors from being thrown.
Within your conditionals you get to remove all of that boilerplate that is involved with checking the existence of properties or their types or anything else. You just end up with this nice clean code.