🔥 Vue Tips #27: Controlled Props — or How to Override Internal State
Tags: vue, webdev, javascript, tutorial
Published: 2021-09-29 15:42:59

---
title: "🔥 Vue Tips #27: Controlled Props — or How to Override Internal State"
published: true
description: "Overriding internal state with controlled props, computed props directive for your template, another secret of computed props, working code, and dealing with shared state."
canonical_url: https://michaelnthiessen.com/weekly-027-september-22/
tags: vue, webdev, javascript, tutorial
series: Vue Tips
---

_This newsletter was sent out to my list on September 22, 2021. [Sign up here](https://michaelnthiessen.com/newsletter) to get emails like this each week!_

Hi!

First, I need to say a huge THANK YOU to all 212 of you who bought Clean Components in the sale that ended last week.

Your support of my work is absolutely astonishing — it's what gives me the motivation to keep creating.

So, it's officially the first day of fall, at least in the northern hemisphere.

Everything seems to be pumpkin-spiced these days:
- lattes from Starbucks
- scented candles (why)
- Cheerios (okay, I'm willing to try that one)

Please tell me this pumpkin obsession is just a North American thing?

I'll spare you from pumpkin-spiced tips though. These ones are just the regular kind 🎃

— Michael


## 🔥 Controlled Props — or How to Override Internal State

Here we have a simple `Toggle` component that can show or hide content:
```html
<template>
  <Toggle title="Toggled Content">
    This content can be hidden by clicking on the toggle.
  </Toggle>
</template>
```

It keeps track of its own `open` state internally right now.

But what if we want to override that internal state, but only _some of the time_?

To do this we have to dynamically switch between relying on props and events, and relying on internal state:

```js
export default {
  name: 'Toggle',

  props: {
    title: {
      type: String,
      required: true,
    },
    hidden: {
      type: Boolean,
      // Must be set to `undefined` and not `false`
      default: undefined,
    }
  },

  data() {
    return {
      // Internal state
      _hidden: false,
    };
  },

  methods: {
    toggleHidden() {
      // Switch between emitting an event and toggling state
      if (this.hidden !== undefined) {
        this.$emit('toggle-hidden');
      } else {
        this._hidden = !this._hidden;
      }
    },
  },

  computed: {
    $hidden() {
      // Dynamically switch between state or prop
      return this.hidden !== undefined
        ? this.hidden
        : this._hidden;
    },
  },
};
```

In the `Toggle` component we now have to use the `$hidden` computed prop:
```html
<template>
  <div>
    <div
      class="title"
      @click="toggleHidden"
    >
      {{ title }}
    </div>
    <slot v-if="$hidden" />
  </div>
</template>
```

You can check out a [more detailed tutorial on my blog](https://michaelnthiessen.com/controlled-props-pattern).


## 🔥 Computed Props in Your Template: v-memo

Vue 3.2 gives you fine-grained control over template re-rendering using `v-memo`:
```html
<div v-memo="[varA, varB, varC]">
  <!-- ... -->
</div>
```

This works much the same as a computed prop does. An element with `v-memo` is only re-rendered when the array changes, but otherwise it caches (or memoizes) the result.

When it's used with `v-for` you can selectively re-render only the parts of a list that have changed:
```html
<div
  v-for="item in list"
  :key="item.id"
  v-memo="[item.id === selected]"
>
  <!-- ... -->
</div>
```

Here, we only update the nodes that went selected to unselected, or vice versa. Much faster if you're dealing with extremely long lists!

But since Vue is already so efficient with re-renders, you shouldn't need to use `v-memo` very often.

It's definitely a useful tool to help you get more performance — when you really need it.

Check out [the docs for v-memo](https://v3.vuejs.org/api/directives.html#v-memo).


## 🔗 (Sponsored) Come work with me at Vidyard

It's where I've learned all that I know about Vue.

We're rapidly growing our remote engineering team and we need intermediate and senior devs in Canada and the US.

Our stack is Vue + GraphQL + Rails (with React, Node, and some other things, too).

You'll get to work on tons of diverse and interesting projects. Some of the things I've worked on:
- In-house component library — written in Vue, of couse
- Analytics dashboard and pipeline — lots of data, charts, and microservices
- That time we **rewrote our entire frontend in Vue** — yes, we're *very* committed to paying down tech debt
- UI for simple video editing

It's also the best place to work (in my opinion):
- In 4 years I've never had to rush to hit a deadline. No evenings, weekends, or stress-induced baldness.
- Every dev has their own "sandbox" — a replica of our production environment, running in AWS, complete with hot reloading.
- The fundamentals: continuous deployment, e2e testing, mentors that help you grow, a focus on doing things _right_, very few meetings to interrupt your flow, too many memes, never enough memes.

[Apply now and get the best job you've ever had.](https://www.vidyard.com/careers/#positions)


## 📜 I didn't know this about computed props

Computed props may not work _exactly_ how you think they do.

That's what a lot of us are finding out after reading Thorsten's post on a common misconception around rendering and computed props.

It's not something you'll run into every day, but it's good to know about if you run into performance problems.

Read it here: [When a computed property can be the wrong tool](https://dev.to/linusborg/vue-when-a-computed-property-can-be-the-wrong-tool-195j)


## 💬 Working
_The greatest performance improvement of all is when a system goes from not-working to working. — John Ousterhout_

First, get it to work. Then get it to work well. Don't worry about how good the code is until it's working — that's what refactoring is for.

It's _so much easier_ to transform bad code into good code if it's already working correctly.


## 🗞 News: Vue.js Amsterdam update

Unfortunately, Vue.js Amsterdam [had to cancel](https://twitter.com/vuejsamsterdam/status/1437687577667121156?s=20) their conference for this October due to COVID, but it's on for February 2022!

Here are some upcoming conferences:
- [Vue.js Conference (London)](https://vuejs.london/) — October 20-21 — London and online
- [VueConf Toronto](https://www.vuetoronto.com/) — November 22-23 — free + online
- [Vuejs Global (Amsterdam)](https://www.vuejs.amsterdam/) — February 10-11, 2022 — Amsterdam


## 🧠 Spaced-repetition: Where do you put shared state?

__The best way to commit something to long-term memory is to periodically review it, gradually increasing the time between reviews 👨‍🔬__

__Actually remembering these tips is much more useful than just a quick distraction, so here's a tip from a couple weeks ago to jog your memory.__

Let's say we have a `Button` component that toggles an `Accordion` open and closed by changing the variable `isOpen`.

But the `Button` component changes it's text between "Show" and "Hide" based on the same variable, `isOpen`:
```html
// Parent.vue
<template>
  <!-- Both components need access to `isOpen`  -->
  <Button :is-open="isOpen" @click="toggleOpen" />
  <Accordion :is-open="isOpen">
    Some interesting content in here.
  </Accordion>
</template>
```

These two sibling components (because they are beside each other) need access to the same state, so where do we put it?

Answer: The lowest common ancestor!

Which, in this case, is the parent of both components.

Because state only flows down through props, shared state _must_ be in a common ancestor. And we also want to keep state as close as possible, so we put it in the _lowest_ common ancestor.

While this example may seem obvious to some, when the components sharing state are in separate components, in separate folders, it's harder to see that this is the solution.

Note: we also want to co-locate state with the logic that modifies it, so we have to put the `toggleOpen` method in the parent as well.

## Exclusive tips and insights every week

Join 8135 other Vue devs and [get exclusive tips and insights](michaelnthiessen.com/newsletter) like these delivered straight to your inbox, every week.

> You have great content in your emails. I seriously learn something from every one of them. — Titus Decali

> Thanks for another beautiful tip 🙏 — Victor Onuoha

> Loving these, and the spaced repetition — Mark Goldstein

[Sign up here](michaelnthiessen.com/newsletter)