Search code examples
vuejs3vue-composition-apivue-script-setup

Component isn't updating from computed value


I have a generic component linked to a computed value to simulate routing. There is a current page property, and three links, whenever the user clicks on a link, that property gets updated via the renderPage.

Before switching to <script setup> the code worked fine, now, the generic component doesn't seem to do anything.

Here's the code:

<script setup>
import { computed, ref } from "vue";
import HomePage from "./components/HomePage.vue";
import LoginPage from "./components/LoginPage.vue";
import UsersPage from "./components/UsersPage.vue";


const currentPage = ref("Home");

const renderPage = computed(() => `${currentPage.value}Page`)


function showHomePage() {
  currentPage.value = "Home";
}
function showLoginPage() {
  currentPage.value = "Login";
}
function showUsersPage() {
  currentPage.value = 'Users';
}
</script>

<template>
  <header class="header">
    <span class="logo">
      <img src="@/assets/vue-heart.png" width="30" />C'est La Vue
    </span>
    <nav class="nav">
      <a href="#" @click.prevent="showHomePage">Home</a>
      <a href="#" @click.prevent="showUsersPage">Users</a>
      <a href="#" @click.prevent="showLoginPage">Login</a>
    </nav>
  </header>
  <component :is="renderPage" />
</template>

The generic component is able to render the components properly if set manually, but whenever linked to the renderPage computed property, it doesn't work anymore.

The renderPage when added inside a mustache works as expected, updating the string as it should.


Solution

  • import HomePage from "./components/HomePage.vue"; make a variable HomePage, not something in the the template context named HomePage. To refer to an imported component by name, make an object containing components by name:

    const components = {HomePage, LoginPage, UserPage};
    

    Then use it in the template:

      <component :is="components[renderPage]" />
    

    But better is to reference the components directly:

    
    <script setup>
    import { computed, ref } from "vue";
    import HomePage from "./components/HomePage.vue";
    import LoginPage from "./components/LoginPage.vue";
    import UsersPage from "./components/UsersPage.vue";
    
    
    const renderPage = ref(HomePage);
    
    
    </script>
    
    <template>
      <header class="header">
        <span class="logo">
          <img src="@/assets/vue-heart.png" width="30" />C'est La Vue
        </span>
        <nav class="nav">
          <a href="#" @click.prevent="renderPage = HomePage">Home</a>
          <a href="#" @click.prevent="renderPage = UserPage">Users</a>
          <a href="#" @click.prevent="renderPage = LoginPage">Login</a>
        </nav>
      </header>
      <component :is="renderPage" />
    </template>