Lonely Children

Control flow directives, like v-if and v-for, can create lots of nested code that's difficult to read.

This is particularly true if you have a v-for nested inside of a v-if (which itself might be nested inside of a v-for).

For your sake, I hope you don't have to deal with code that has that many levels of nesting.

But if you do, I've got something you'll like.

I've picked up this trick that makes it simple to transform these nightmares into fairy tales.

One lonely child component

When nesting is starting to get out of hand, or if I want to keep things nice and clean, my default is to extract code out into a new component.

With v-for and v-if, we'll take everything inside of the directive and turn it into it's own component.

I'll show you what this looks like.

We start with this template that has some nesting:

<template>
<div>
<h1>{{ title }}</h2>
<div class="navigation">
<!-- ... -->
</div>
<div v-for="item in list">
<h2 class="item-title">
{{ item.title }}
</h2>
<p class="item-description">
{{ item.description }}
</p>
</div>
<footer>
<!-- ... -->
</footer>
</div>
</template>

Then we extract the inner portion into it's own component:

<template>
<div>
<h2 class="item-title">
{{ item.title }}
</h2>
<p class="item-description">
{{ item.description }}
</p>
</div>
</template>

If we pop this new component in, we see that it gets rid of all the nesting quite nicely:

<template>
<div>
<h1>{{ title }}</h2>
<div class="navigation">
<!-- ... -->
</div>
<ListItem
v-for="item in list"
:item="item"
/>
<footer>
<!-- ... -->
</footer>
</div>
</template>

Now, this example doesn't have a lot of nesting in it, and wasn't all that bad to begin with. But if we have two, three, or more levels of nesting, this technique can really clean things up and make the code easier to understand.

This technique, along with other ways of cleaning up your code, is found in my book, Clean Components.

But here's a question for you:

Why don't we include the v-for directive inside of our new component?

Should we include control flow directives?

Imagine that instead of keeping our v-for in the parent component, we pull it out into the new component. This is what we would get:

<template>
<div>
<h1>{{ title }}</h2>
<div class="navigation">
<!-- ... -->
</div>
<List :list="list" />
<footer>
<!-- ... -->
</footer>
</div>
</template>

It turns out that this simplifies the parent a little more, but you probably saw that one coming.

Unfortunately, that complexity is instead pushed into the child component:

<template>
<div>
<div v-for="item in list">
<h2 class="item-title">
{{ item.title }}
</h2>
<p class="item-description">
{{ item.description }}
</p>
</div>
</div>
</template>

Which approach is better?

If all you want in life is to reduce the amount of nesting you have, this option is certainly worse. We added an extra level of nesting in our child component without reducing it in the parent.

If you're a bird, perhaps you prefer this option because of how much you love nests.

(I wasn't sure if this joke would fly...)

Generally, I would avoid putting control flow directives like v-if and v-for at the root of a component like this, because it affects reusability.

What if you wanted just a single ListItem?

If you used a v-if, what if you wanted to use different logic to switch between the components, or you wanted to use entirely different components?

Writing it in this way limits your flexibility without giving you much in return. Because of this, I tend to leave control flow outside of small components like this. I also try to avoid having them at the root of my components.

This issue — and it's solution — becomes more obvious once you dig deeper to understand what's going on "underneath".

Next week we'll do just that, and I think it will help you to understand components — and Vue — on a whole new level.