🔥 (#204) Forcing a component to update, dynamic returns, and more

Read this on my blog

Hey all!

This weeks podcast episode was fun, because I was interviewed by my friend Alex Lichter (next week's episode is the other way around).

I've also been working hard on the big update for Mastering Nuxt! I've been sharing some updates on my social media, so you can follow along there if you'd like.

— Michael

🔥 Forcing a Component to Update

What do you do if a component isn't updating the way it should?

Likely, this is caused by a misunderstanding and misuse of the reactivity system.

But let's look at a quick solution using forceUpdate:

import { getCurrentInstance } from 'vue';    const methodThatForcesUpdate = () => {    // ...    const instance = getCurrentInstance();    instance.proxy.forceUpdate();    // ...  };

This getCurrentInstance is actually an undocumented part of Vue (it was removed from the docs) and is not recommended — the better solution is below.

Using the Options API instead:

export default {    methods: {      methodThatForcesUpdate() {        // ...        this.$forceUpdate();  // Notice we have to use a $ here        // ...      }    }  }

Now, here comes the sledgehammer if the previous approach doesn't work.

I do not recommend using this approach. However, sometimes you just need to get your code to work so you can ship and move on.

But please, if you do this, keep in mind this is almost always the wrong way, and you're adding tech debt in to your project.

We can update a componentKey in order to force Vue to destroy and re-render a component:

<template>    <MyComponent :key="componentKey" />  </template>    <script setup>  import { ref } from 'vue';  const componentKey = ref(0);    const forceRerender = () => {    componentKey.value += 1;  };  </script>

The process is similar with the Options API:

export default {    data() {      return {        componentKey: 0,      };    },    methods: {      forceRerender() {        this.componentKey += 1;      }    }  }

You can find a deeper explanation here: https://michaelnthiessen.com/force-re-render/

🔥 Dynamic Returns

A composable can either return a single value or an object of values. Typically, these values are refs.

But we can also dynamically switch between the two depending on what we want to use the composable for:

// Grab only the single value  const now = useNow()    // Get more granular access to the composable  const {    now,    pause,    resume  } = useNow({ controls: true })

This is great because we may only need a single value most of the time. So why complicate the interface for the main use case?

But by dynamically providing the full object of refs, we allow for more advanced use cases as well. Even if they are rarely used.

Here is how we might implement that:

export default useNow(opts) {    const {      controls = false,    } = opts;      // Do some things in your composable      if (controls) {      return { now, pause, resume };    } else {      return now;    }  }

🔥 h and Render Functions

When using the render function instead of templates, you'll be using the h function a lot:

<script setup>  import { h } from 'vue';  const render = () => h('div', {}, 'Hello Wurld');  </script>

It creates a VNode (virtual node), an object that Vue uses internally to track updates and what it should be rendering.

The first argument is either an HTML element name or a component (which can be async if you want):

<script setup>  import { h } from 'vue';  import MyComponent from './MyComponent.vue';    const render = () => h(MyComponent, {}, []);  </script>

The second argument is a list of props, attributes, and event handlers:

<script setup>  import { h } from 'vue';  import MyComponent from './MyComponent.vue';    const render = () => h(MyComponent, {    class: 'text-blue-400',    title: 'This component is the greatest',    onClick() {      console.log('Clicked!');    },  }, []);  </script>

The third argument is either a string for a text node, an array of children VNodes, or an object for defining slots:

<script setup>  import { h } from 'vue';  import MyComponent from './MyComponent.vue';    const render = () => h(MyComponent, {}, [    'Simple text node',    h('span', {}, 'Text inside of a <span> element'),  ]);  </script>

These render functions are essentially what is happening "under the hood" when Vue compiles your single file components to be run in the browser.

But by writing out the render function yourself, you are no longer constrained by what can be done in a template.

You have the full power of Javascript at your fingertips 🖐

This is just scratching the surface on what render functions and h can do. Read more about them on the official docs.

🎙️ #046 — A Vue at Michael Thiessen

The guest of this episode is one you've heard on here quite often - but do you also know him well?

Together, we take a look (or Vue 👀) at Michael Thiessen, co-host of this podcast.

Learn how his average day looks like, how he became a full-time content creator and find out what he did before getting into Web Development, plus insights on how he created his first course and dealt with impostor syndrome.

Watch on YouTube or listen on your favorite podcast platform.

Chapters:

In case you missed them:

📜 Explaining Nested Routes

Routing is one of the foundational parts of any web app, whether you're using Nuxt or just using Vue Router directly.

In most apps you'll also need to create nested routes (or child routes) — but they can be hard to wrap your head around.

In this article I take a unique angle in explaining how they work.

Check it out here: Explaining Nested Routes

📜 Ref vs Reactive — Which is Best?

This has been a question on the mind of every Vue dev since the Composition API was first released:

What's the difference between ref and reactive, and which one is better?

In this article, I explore this question in detail, comparing ref and reactive, giving my own opinion, and sharing other opinions from the community.

Check it out here: Ref vs Reactive — Which is Best?

📅 Upcoming Events

Here are some upcoming events you might be interested in. Let me know if I've missed any!

Vuejs Amsterdam 2025 — (March 12, 2025 to March 13, 2025)

The biggest Vue conference in the world! A two-day event with workshops, speakers from around the world, and socializing.

Check it out here

VueConf US 2025 — (May 13, 2025 to May 15, 2025)

Giving a talk here on component patterns! A great Vue conference, this year held in Tampa. Two days of conference talks, plus a day for workshops.

Check it out here

MadVue 2025 — (May 29, 2025)

It's time to get together in Madrid. Join for a full day of talks, activities, and networking with the Vue.js community and ecosystem.

Check it out here

💬 More Reliable Features

"If there is a feature of a language that is sometimes problematic, and if it can be replaced with another feature that is more reliable, then always use the more reliable feature." — Douglas Crockford

🧠 Spaced-repetition: SSR Safe Directives

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.

In many cases, we need to generate unique IDs for elements dynamically.

But we want this to be stable through SSR so we don't get any hydration errors.

And while we're at it, why don't we make it a directive so we can easily add it to any element we want?

Here's a stripped-down version of this directive:

const generateID = () => Math.floor(Math.random() * 1000);    const directive = {    getSSRProps() {      return { id: generateID() };    },  }

When using it with Nuxt, we need to create a plugin so we can register the custom directive:

// ~/plugins/dynamic-id.ts  const generateID = () => Math.floor(Math.random() * 1000);    export default defineNuxtPlugin((nuxtApp) => {    nuxtApp.vueApp.directive("id", {      getSSRProps() {        return { id: generateID() };      },    });  });

In Nuxt 3.10+, you can also use the useId composable instead:

<template>    <div :id="id" />  </template>    <script setup>  const id = useId();  </script>

Normally, custom directives are ignored by Vue during SSR because they typically are there to manipulate the DOM. Since SSR only renders the initial DOM state, there's no need to run them, so they're skipped.

But there are some cases where we actually need the directives to be run on the server, such as with our dynamic ID directive.

That's where getSSRProps comes in.

It's a special function on our directives that is only called during SSR, and the object returned from it is applied directly to the element, with each property becoming a new attribute of the element:

getSSRProps(binding, vnode) {    // ...      return {      attribute,      anotherAttribute,    };  }

🔗 Want more Vue and Nuxt links?

Michael Hoffman curates a fantastic weekly newsletter with the best Vue and Nuxt links.

Sign up for it here.

p.s. I also have a bunch of products/courses:

Unsubscribe

评论

此博客中的热门博文

🔥 (#155) A Vue podcast?

Scripting News: Monday, November 20, 2023